<<

Professional Wordpress® Design and Development

Professional Wordpress® Design and Development

ffirs.indd i 04/12/12 5:12 PM PROFESSIONAL WORDPRESS®: DESIGN AND DEVELOPMENT, SECOND EDITION

INTRODUCTION ...... xxi

CHAPTER 1 First Post ...... 1 CHAPTER 2 Code Overview...... 21 CHAPTER 3 Working with WordPress Locally ...... 41 CHAPTER 4 Tour of the Core ...... 57 CHAPTER 5 The Loop ...... 73 CHAPTER 6 Data Management ...... 101 CHAPTER 7 Custom Post Types, Custom Taxonomies, and Metadata ...... 115 CHAPTER 8 Plugin Development ...... 139 CHAPTER 9 Theme Development ...... 211 CHAPTER 10 Multisite ...... 259 CHAPTER 11 Content Aggregation ...... 289 CHAPTER 12 Crafting a User Experience ...... 309 CHAPTER 13 Statistics, Scalability, Security, and Spam ...... 337 CHAPTER 14 WordPress as a Content Management System ...... 365 CHAPTER 15 WordPress in the Enterprise ...... 383 CHAPTER 16 WordPress Developer Community ...... 397

INDEX ...... 411

ffirs.indd i 04/12/12 5:12 PM ffirs.indd ii 04/12/12 5:12 PM PROFESSIONAL WordPress ®

ffirs.indd iii 04/12/12 5:12 PM ffirs.indd iv 04/12/12 5:12 PM PROFESSIONAL WordPress ® DESIGN AND DEVELOPMENT Second Edition

Brad Williams David Damstra Hal Stern

John Wiley & Sons, Inc.

ffirs.indd v 04/12/12 5:12 PM Professional WordPress®: Design and Development, Second Edition Published by John Wiley & Sons, Inc. 10475 Crosspoint Boulevard Indianapolis, IN 46256 www.wiley.com Copyright © 2013 by John Wiley & Sons, Inc., Indianapolis, Indiana Published simultaneously in Canada ISBN: 978-1-118-44227-2 ISBN: 978--1118-44229-6 (ebk) ISBN: 978-1-118-60438-0 (ebk) ISBN: 978-1-118-60423-6 (ebk) Manufactured in the United States of America 10 9 8 7 6 5 4 3 2 1 No part of this publication may be reproduced, stored in a retrieval system or transmitted in any or by any means, electronic, mechanical, photocopying, recording, scanning or otherwise, except as permitted under Sections 107 or 108 of the 1976 United States Copyright Act, without either the prior written permission of the Publisher, or authorization through payment of the appropriate per-copy fee to the Copyright Clearance Center, 222 Rosewood Drive, Danvers, MA 01923, (978) 750-8400, fax (978) 646-8600. Requests to the Publisher for permission should be addressed to the Permissions Department, John Wiley & Sons, Inc., 111 River Street, Hoboken, NJ 07030, (201) 748-6011, fax (201) 748-6008, or online at http://www.wiley.com/go/permissions. Limit of Liability/Disclaimer of Warranty: The publisher and the author make no representations or warranties with respect to the accuracy or completeness of the contents of this work and specifi cally disclaim all warranties, including without limitation warranties of fi tness for a particular purpose. No warranty may be created or extended by sales or pro- motional materials. The advice and strategies contained herein may not be suitable for every situation. This work is sold with the understanding that the publisher is not engaged in rendering legal, accounting, or other professional services. If professional assistance is required, the services of a competent professional person should be sought. Neither the pub- lisher nor the author shall be liable for damages arising herefrom. The fact that an organization or Web site is referred to in this work as a citation and/or a potential source of further information does not mean that the author or the publisher endorses the information the organization or Web site may provide or recommendations it may make. Further, readers should be aware that Web sites listed in this work may have changed or disappeared between when this work was written and when it is read. For general information on our other products and services please contact our Customer Care Department within the United States at (877) 762-2974, outside the United States at (317) 572-3993 or fax (317) 572-4002. Wiley publishes in a variety of print and electronic formats and by print-on-demand. Some material included with standard print versions of this book may not be included in e-books or in print-on-demand. If this book refers to media such as a CD or DVD that is not included in the version you purchased, you may download this material at http://booksupport.wiley.com. For more information about Wiley products, visit www.wiley.com. Library of Congress Control Number: 2012950504 Trademarks: Wiley, the Wiley logo, Wrox, the Wrox logo, Wrox to Programmer, and related trade dress are trademarks or registered trademarks of John Wiley & Sons, Inc. and/or its affi liates, in the United States and other coun- tries, and may not be used without written permission. WordPress is a registered trademark of WordPress Foundation. All other trademarks are the property of their respective owners. John Wiley & Sons, Inc., is not associated with any product or vendor mentioned in this book.

ffirs.indd vi 04/12/12 5:12 PM For my wife, my partner, my best friend April Williams. You’ll never know how much you mean to me. Thank you for putting up with my nerdy ways and always supporting me.

—Brad Williams

For my loving wife Holly, my children - Jack, Justin and Jonah. Thanks for your love and support.

—David Damstra

For Toby, whose patience grows with each project.

—Hal Stern

ffirs.indd vii 04/12/12 5:12 PM CREDITS

EXECUTIVE EDITOR PRODUCTION MANAGER Carol Long Tim Tate

PROJECT EDITOR VICE PRESIDENT AND EXECUTIVE GROUP Christina Haviland PUBLISHER Richard Swadley TECHNICAL EDITOR Hal Stern VICE PRESIDENT AND EXECUTIVE PUBLISHER Neil Edde PRODUCTION EDITOR Daniel Scribner ASSOCIATE PUBLISHER Jim Minatel COPY EDITOR Nancy Rapoport PROJECT COORDINATOR, COVER Katie Crocker EDITORIAL MANAGER Mary Beth Wakefi eld PROOFREADER Sarah Kaikini, Word One FREELANCER EDITORIAL MANAGER Rosemarie Graham INDEXER Robert Swanson ASSOCIATE DIRECTOR OF MARKETING David Mayhew COVER DESIGNER Elizabeth Brooks MARKETING MANAGER Ashley Zurcher COVER IMAGE © Karen Phillips / iStockphoto BUSINESS MANAGER Amy Knies

ffirs.indd viii 04/12/12 5:12 PM ABOUT THE AUTHORS

BRAD WILLIAMS is the co-founder of WebDevStudios.com, a cohost on the WP Late Night podcast, and the coauthor of Professional WordPress and Professional WordPress Plugin Development. Brad has been developing for over 15 years, including the last 5 where he has focused on open-source technologies like WordPress. Brad has given presentations at various WordCamps across the country and is a co-organizer of the Philadelphia WordPress Meetup and WordCamp Philly. You can follow Brad online on his personal at http://strangework.com and on @williamsba.

DAVID DAMSTRA is a vice president of Professional Services for CU*Answers, a credit union service organization. David manages a team of developers to create websites and web applications for the fi nancial industry. David’s team uses WordPress as the foundation for many web projects. David is also a Zend Certifi ed Engineer for PHP5. You can fi nd David online professionally at http:// ws.cuanswers.com where he focuses on web technology and best practices for web development, especially pertaining to the credit union industry, and personally at http://mirmillo.com where he talks about his family and home brewing.

HAL STERN is a Vice President with a major technology company focusing on software architecture for programmable networks and architectures for “big data” applications. Hal began blogging as part of a corporate communications effort at Sun Microsystems, and has been using WordPress to share thoughts on music, sports, food, and New Jersey for the past fi ve years. Hal’s affi nity for WordPress internals began when he was trying to determine how a mangled URL returned almost- correct content, and that curiosity has turned into his contributions to this book and a WordCamp talk. Hal is online at http://snowmanonfire.com and @freeholdhal.

ffirs.indd ix 04/12/12 5:12 PM ACKNOWLEDGMENTS

THANK YOU to the love of my life, April, for your endless support, friendship, and continuing to put up with my nerdy ways. Thank you to my awesome nieces, Indiana Brooke and Austin Margaret. Thank you to the entire WordPress community for your support, friendships, motivation, and guidance. Thank you to Michael, Jason, Freddy, and Hannibal for always lurking in the shadows. Last but not least, thank you to my ridiculous zoo: Lecter, Clarice, and Squeaks the Cat (aka Kitty Galore). Your smiling faces and wiggly butts always put a smile on my face. —Brad Williams

ffirs.indd x 04/12/12 5:12 PM CONTENTS

INTRODUCTION xxi

CHAPTER 1: FIRST POST 1

What Is WordPress? 1 Popularity of WordPress 3 Current State 3 Intersecting the Community 4 WordPress and the GPL 5 Content and Conversation 6 WordPress as a Content Management System 6 Creating Conversation 7 Getting Started 8 Hosting Options 8 Do It Yourself Installation 10 Finishing Up 17 First-Time Administration 17 First Post 19 Summary 20

CHAPTER 2: CODE OVERVIEW 21

Downloading 21 Download Locations 21 Available Formats 22 Release Archive 22 Directory and File Structure 23 WordPress Confi guration 24 wp-confi g. File 24 Advanced wp-confi g Options 26 .htaccess 31 The .maintenance File 35 wp-content User Playground 36 Plugins 36 Themes 37 Uploads and Media Directory 37 Upgrade Directory 38

ftoc.indd xi 04/12/12 9:18 AM CONTENTS

Custom Directories 38 Summary 39

CHAPTER 3: WORKING WITH WORDPRESS LOCALLY 41

Benefi ts of Working Locally 41 Typical Deployment Cycle 42 Why So Much Process? 42 Tools for Component Administration 43 Getting Your Development Stack 44 Adding WordPress to the Local Install 45 Confi guration Details 46 Managing the Document Tree 46 Enabling Debug Information 48 Handling Local and Production Database 50 Creating Virtual Local Server Names 50 Local Theme and Plugin Development 53 Deploying Local Changes 53 Summary 55

CHAPTER 4: TOUR OF THE CORE 57

What’s in the Core? 57 Using the Core as a Reference 58 Inline Documentation 59 Finding Functions 60 Exploring the Core 62 Deprecated Functions 65 WordPress Codex and Resources 66 What Is the Codex? 66 Using the Codex 66 Function Reference 67 WordPress APIs 69 Codex Controversy 71 Don’t Hack the Core! 71 Why Not? 71 Alternatives to Hacking the Core 72 Summary 72

CHAPTER 5: THE LOOP 73

Understanding the Loop 74 From Query Parameters to SQL 75

xii

ftoc.indd xii 04/12/12 9:18 AM CONTENTS

Understanding Content in WordPress 76 Putting the Loop in Context 76 Flow of the Loop 77 Template Tags 79 Commonly Used Template Tags 80 Tag Parameters 81 Customizing the Loop 81 Using the WP_Query Object 82 Building a Custom Query 83 Adding Paging to a Loop 85 Using query_posts( ) 86 Using get_posts( ) 87 Resetting a Query 88 More Than One Loop 90 Advanced Queries 91 Global Variables 93 Post Data 93 Author Data 94 User Data 95 Environmental Data 95 Global Variables or Template Tags? 96 Working Outside the Loop 97 Summary 100

CHAPTER 6: DATA MANAGEMENT 101

Database Schema 101 Table Details 103 WordPress Content Tables 104 WordPress Taxonomy Tables 105 WordPress Database Class 106 Simple Database Queries 106 Complex Database Operations 108 Dealing with Errors 110 Direct Database Manipulation 111 Summary 114

CHAPTER 7: CUSTOM POST TYPES, CUSTOM TAXONOMIES, AND METADATA 115

Understanding Data in WordPress 115 What Is a Custom Post Type? 116 Register Custom Post Types 116

xiii

ftoc.indd xiii 04/12/12 9:18 AM CONTENTS

Setting Post Type Labels 121 Working with Custom Post Types 122 Custom Post Type Template Files 123 Special Post Type Functions 124 WordPress Taxonomy 126 Default Taxonomies 126 Taxonomy Table Structure 126 Understanding Taxonomy Relationships 127 Building Your Own Taxonomies 128 Custom Taxonomy Overview 128 Creating Custom Taxonomies 128 Setting Custom Taxonomy Labels 131 Using Your Custom Taxonomy 132 Metadata 133 What Is Metadata? 134 Adding Metadata 134 Updating Metadata 135 Deleting Metadata 135 Retrieving Metadata 136 Summary 137

CHAPTER 8: PLUGIN DEVELOPMENT 139

Plugin Packaging 140 Creating a Plugin File 140 Creating the Plugin Header 140 Plugin License 141 Activating and Deactivating Functions 142 Internationalization 143 Determining Paths 145 Plugin Security 147 Nonces 147 Data Validation and Sanitization 148 Know Your Hooks: Actions and Filters 151 Actions and Filters 151 Popular Filter Hooks 153 Popular Action Hooks 154 Plugin Settings 156 Saving Plugin Options 156 Array of Options 157 Creating a Menu and Submenus 158 Creating an Options Page 160 WordPress Integration 169

xiv

ftoc.indd xiv 04/12/12 9:18 AM CONTENTS

Creating a Meta Box 169 Shortcodes 174 Creating a Widget 175 Creating a Dashboard Widget 179 Creating Custom Tables 180 Uninstalling Your Plugin 182 Creating a Plugin Example 184 Publishing to the Plugin Directory 203 Restrictions 204 Submitting Your Plugin 204 Creating a readme.txt File 204 Setting Up SVN 208 Publishing to the Plugin Directory 209 Releasing a New Version 210 Summary 210

CHAPTER 9: THEME DEVELOPMENT 211

Why Use a Theme? 211 Installing a Theme 212 FTP Installation 212 Theme Installer 213 What Is a Theme? 213 Template Files 214 CSS 214 Images and Assets 214 Plugins 215 Creating Your Own Theme 215 Project Themes vs. Child Themes 215 What to Look for in a Starter Theme 216 Creating Your Own Theme: Getting Started 217 Essential File: Style.css 217 Showing Your Content: Index.php 218 Showing Your Content in Diff erent Ways: Index.php 220 Creating Your Own Theme: DRY 220 Header.php 221 Footer.php 222 Sidebar.php 222 Deviations from the Norm: Conditional Tags 223 Creating Your Own Theme: Content Display 224 Customizing Your Homepage: Front-Page.php 225 Show Your Older Posts by Date: Archive.php 227 Showing Only One Category: Category.php 228

xv

ftoc.indd xv 04/12/12 9:18 AM CONTENTS

Show Posts of a Specifi c Tag: Tag.php 230 Other Archival Templates 231 How to Show a Single Post: Single.php 231 Display a Page: Page.php 232 Display Post Attachments: Attachment.php 233 Template Hierarchy 233 Creating Your Own Theme: Additional Files 235 Handle 404 Errors: 404.php 235 Author.php 236 Comments.php 237 Adding Functionality to Your Templates: Functions.php 238 Search.php 240 SearchForm.php 242 Other Files 242 Custom Page Templates 243 When to Use Custom Page Templates 243 How to Use Custom Page Templates 244 Stock Twenty Eleven Page Templates 245 Other Theme Enhancements 246 Menu Management 246 Widget Areas 248 Post Formats 249 Theme Settings 250 Theme Customizer 251 Theme Hierarchy and Child Themes 251 Premium Themes and Other Theme Frameworks 256 Bones Theme 256 Carrington Theme 257 Genesis Theme 257 Hybrid Core Theme 257 Roots 257 StartBox Theme 258 Thematic Theme 258 Summary 258

CHAPTER 10: MULTISITE 259

What Is Multisite? 259 Multisite Terminology 260 Diff erences 260 Advantages of Multisite 261 Enabling Multisite 261 Working in a Network 262

xvi

ftoc.indd xvi 04/12/12 9:18 AM CONTENTS

Network Admin 263 Creating and Managing Sites 263 Working with Users and Roles 264 Themes and Plugins 264 Settings 265 Domain Mapping 265 Coding for Multisite 265 Blog ID 265 Common Functions 266 Creating a New Site 270 Network Admin Menus 274 Multisite Options 276 Users in a Network 282 Super Admins 285 Network Stats 286 Multisite Database Schema 287 Multisite-Specifi c Tables 287 Site-Specifi c Tables 287 Summary 288

CHAPTER 11: CONTENT AGGREGATION 289

Getting Noticed 290 Social Media Buttons 291 Feeding WordPress Upstream 292 Buttons, Badges, or Both? 292 Simple Social Networking Badges 293 Collecting External Content 294 Integrating a YouTube Video 295 Integrating Twitter 296 Google Maps 298 Integrating 299 Generic XML Data 299 Transients 301 Advertising 303 Monetizing Your Site 303 Setting Up Advertising 304 Privacy and History 307 Summary 308

CHAPTER 12: CRAFTING A USER EXPERIENCE 309

User Experience Principles 309 Consistent Navigation 310 xvii

ftoc.indd xvii 04/12/12 9:18 AM CONTENTS

Visual Design Elements 312 Making Content Easy to Find 314 Site Load Times 314 Using JavaScript 316 Usability and Usability Testing 316 Structuring Your Information 318 Getting Your Site Found 320 Duplicate Content 321 Trackbacks and Pings 323 Tags and Content Sharing Sites 324 How Web Standards Get Your Data Discovered 324 Semantic HTML 324 Valid HTML 326 Microformats 327 HTML5 329 CSS3 330 Searching Your Own Site 331 Weaknesses of the Default Search 331 Alternatives and Plugins to Help 332 Mobile Access and Responsive 334 Leave It Alone 334 Lightweight Mobile 335 Responsive Design 335 Summary 336

CHAPTER 13: STATISTICS, SCALABILITY, SECURITY, AND SPAM 337

Statistics Counters 337 AWStats 338 Google Analytics 340 JetPack by WordPress.com 342 Cache Management 343 WordPress System Complexity 344 Web Server Caching and Optimization 345 WordPress Object Caching 347 Transient Caches 347 MySQL Query Cache 348 Load Balancing Your WordPress Site 349 Dealing with Spam 350 Comment Moderation and CAPTCHAs 350 Automating Spam Detection 351 Securing Your WordPress Site 352

xviii

ftoc.indd xviii 04/12/12 9:18 AM CONTENTS

Staying Up-to-Date 352 Hiding WordPress Version Information 353 Limit Login Attempts 354 Using Good Passwords 354 Changing Your Table Prefi x 354 Moving Your Confi guration File 354 Moving Your Content Directory 355 Using the Secret Key Feature 355 Forcing SSL on Login and Admin 356 Apache Permissions 356 MySQL Credentials 357 Recommended Security Plugins 357 Using WordPress Roles 360 Subscriber Role 361 Contributor Role 361 Author Role 361 Editor Role 361 Administrator Role 362 Super Admin Role 362 Role Overview 362 Extending Roles 363 Summary 364

CHAPTER 14: WORDPRESS AS A CONTENT MANAGEMENT SYSTEM 365

Defi ning Content Management 365 Workfl ow and Delegation 367 User Roles and Delegation 367 Workfl ow 368 Content Organization 370 Theme and Widget Support 370 Homepages 372 Featured Content Pages 373 Content Hierarchy 376 Interactivity Features 379 Forums 379 Forms 379 E-Commerce 380 Other Content Management Systems 380 WordPress Integration 381 Where Not to Use WordPress 381 Summary 382

xix

ftoc.indd xix 04/12/12 9:18 AM CONTENTS

CHAPTER 15: WORDPRESS IN THE ENTERPRISE 383

Is WordPress Right for Your Enterprise? 383 When WordPress Isn’t Right for You 385 Scalability 386 Performance Tuning 386 Caching 388 Regular Maintenance 388 Hardware Scaling 389 Integration with Enterprise Identity Management 391 LDAP and Active Directory 391 OpenID and OAuth 392 Content Integration via Feeds 393 Summary 395

CHAPTER 16: WORDPRESS DEVELOPER COMMUNITY 397

Contributing to WordPress 397 Understanding Trac 398 Working on the Core 401 Submitting Plugins and Themes 402 Documentation 402 Sister Projects 403 BuddyPress 403 bbPress 403 Future Projects 403 Resources 404 Codex 404 Support Forums 404 WordPress Chat 405 Mailing Lists 405 External Resources 406 WordCamp and Meetups 407 WordPress.TV 407 Theme/Plugin Directories 407 WordPress Ideas 407 WordPress Development Updates 408 Make WordPress.org 408 WordPress Podcasts 408 WordPress News Sites 409 Summary 410

INDEX 411

xx

ftoc.indd xx 04/12/12 9:18 AM INTRODUCTION

DEAR READER, Thank you for picking up this book. WordPress is the most popular self-hosted software in use today. It is available as an open source project, licensed under the GPL, and is built largely on top of the MySQL database and PHP programming language. Any server envi- ronment that supports that simple combination can run WordPress, making it remarkably portable as well as simple to install and operate. You don’t need to be a systems administrator, developer, HTML expert, or design aesthete to use WordPress. On the other hand, because WordPress has been developed using a powerful set of Internet standard platforms, it can be extended and tailored for a wide variety of applications. WordPress is the publishing mechanism underneath thousands of individual blog voices and the engine that powers high-volume, high-profi le sites such as CNN’s websites and . It was designed for anyone comfortable navigating a browser, but is accessible to web designers and developers as well. Given this range of applications and capabilities, it can prove hard to know where to start if you want to make use of the power of WordPress for your specifi c purposes. Should you fi rst study the database models and relationships of content and metadata, or the presentation mechanics that generate the HTML output? This book was designed for readers to develop a knowledge of WordPress from the inside out, focusing on the internal structure and fl ow of the core code as well as the data model on which that code operates. Knowing how something works often makes you more adept at working with it, extending it, or fi xing it when it breaks. Just as a race car driver benefi ts from a fundamental knowledge of combustion engines, aerodynamics, and the mechanics of automobile suspension, someone driving WordPress through its full dynamic range will be signifi cantly more adept once acquainted with the underlying software physics.

WHO THIS BOOK IS FOR

It was the dichotomy between the almost trivial effort required to create a WordPress-based website and publish a “fi rst post” to the world and the much more detailed, broad understanding required to effect mass customization that led us to write this book. Many books on the market provide guidance to beginning bloggers by walking you through the typical functions of creating, confi gur- ing, and caring for your WordPress site. Our goal was to bridge the gap between an expert PHP developer who is comfortable reading the WordPress Codex in lieu of a manual and the casual WordPress user creating a public persona integrated with social networking sites and advertising services, with a tailored look and feel. In short, we hope to appeal to a range of developers, from the person looking to fi ne-tune a WordPress theme to a more advanced developer with a plugin concept or who is using WordPress in a large enterprise integrated into a content management system. We do this by exploring WordPress from the inside out. Our goal for this book is to describe the basic operation of a function, and

flast.indd xxi 04/12/12 9:18 AM INTRODUCTION

then offer guidance and examples that highlight how to take it apart and reassemble that function to fi t a number of needs. WordPress users who are not hardened PHP developers may want to skim through the developer-centric section, whereas coders looking for specifi c patterns to implement new WordPress functionality can start in the middle and work toward the end.

WHAT THIS BOOK COVERS

This book is divided into three major sections: Chapters 1 through 4 are an overview of the WordPress system, its major functional elements, and a top-level description of what happens when a WordPress-generated web page is displayed. Chapters 5 through 9 build on this foundation and dive into the core of WordPress, describing internal code fl ow and data structures. This middle section is strongly developer-oriented, and describes how to extend WordPress through plugins and customize it via themes. The last section, Chapters 10 through 16, combines a developer view of user experience and optimization with the deployer requirements for performance, security, and enterprise integration.

HOW THIS BOOK IS STRUCTURED

The following is a detailed chapter-by-chapter overview of what you can expect to fi nd in this book. Chapter 1, “First Post,” contains a brief summary of the history of the WordPress software core, explores some popular hosting options, why community matters in a content-centric world, and concludes with the basics of do-it-yourself WordPress installation and debugging. Chapter 2, “Code Overview,” starts with the mechanics of downloading the WordPress distribution and describes its basic contents and fi lesystem layout. A top-to-bottom code fl ow walks you from an index or specifi c post URL, through the process of selecting posts, assembling content, and generating the displayed HTML. This chapter is a map for the more detailed code tours in the developer-focused section. Chapter 3, “Working with WordPress Locally,” covers the many benefi ts to working with WordPress on your local computer. This chapter also reviews the various setups for local development on a Microsoft Windows or Apple OSX computer. Finally you’ll cover how to deploy your local changes to a remote server using various deployment methods. Chapter 4, “Tour of the Core,” examines the essential PHP functions comprising the basic WordPress engine. It serves as an introduction to the developer-focused middle section of the book and also lays the foundation for the deployment-, integration-, and experience-focused chapters in the last section. This chapter also covers using the core as a reference guide, and why it is best not to hack the core code to achieve desired customizations. Chapter 5, “The Loop,” is the basis for the developer-centric core of this book. The WordPress main loop drives the functions of creating and storing content in the MySQL database, as well as extracting appropriate chunks of it to be sorted, decorated, and nested under banners or next to sidebars, in

xxii

flast.indd xxii 04/12/12 9:18 AM INTRODUCTION

both cases generating something a web browser consumes. This chapter disassembles those processes of creating, saving, and publishing a new post as well as displaying content that has been stored in the WordPress MySQL databases. The underlying database functions and the management of content metadata are covered in more detail to complete a thorough view of WordPress’ internal operation. Chapter 6, “Data Management,” is the MySQL-based counterpart to Chapter 5. The core functions create, update, and manipulate entries in multiple MySQL database tables, and this chapter covers the database schema, data and metadata taxonomies used, and the basic relations that exist between WordPress elements. It also includes an overview of the basic query functions used to select and extract content from MySQL, forming a basis for extensions and custom code that needs to be able to examine the individual data underlying a WordPress site. Chapter 7, “Custom Post Types, Custom Taxonomies, and Metadata,” explores the different types of content and associated data in WordPress. You’ll cover how to register and work with custom post types for creating custom content in WordPress. Custom taxonomies are also dissected, diving into the various setups with examples. Finally you’ll cover post metadata, and the proper ways to store arbitrary data against posts in WordPress. Chapter 8, “Plugin Development,” starts with the basic plugin architecture and then explores the hook, action, and fi lter interfaces that integrate new functionality around the WordPress core. This chapter demonstrates the interposition of functions into the page composition or content management streams and how to save plugin data. Examples of building a plugin using a simple framework outline the necessary functionality of any plugin. This chapter also covers creation of widgets, simpler-to-use plugins that typically add decoration, additional images, or content to a sidebar; many plugins also have a widget for easier management. Publishing a plugin to the WordPress repository and pitfalls of plugin confl ict round out the discussion of WordPress’ functional extensions. Chapter 9, “Theme Development,” is the display and rendering counterpart to Chapter 8. Plugins add new features and functions to the core, whereas themes, CSS and page templates change the way that content is shown to readers. Starting with a basic theme, this chapter covers writing a theme, building custom page templates, menu management, widget areas, post formats, theme installation, and how thematic elements are used by the functions described in previous chapters. This chapter ends the deep developer-focused middle section of the book. Chapter 10, “Multisite,” explores the popular Multisite feature of WordPress. You’ll learn the advantages of running your own Multisite network, how to properly install Multisite, working in a network, creating sites and users, managing themes and plugins, and even domain mapping. The last part of the chapter explores coding for Multisite and the various functions and methods avail- able for use. Chapter 11, “Content Aggregation,” looks at WordPress from a services point of view. If a website represents your public persona or online presence, it has to pull content from a variety of tools and content sources. This chapter delves into web services interfaces, WordPress APIs, feeds into and out of WordPress, and making WordPress entries show up in Facebook pages.

xxiii

flast.indd xxiii 04/12/12 9:18 AM INTRODUCTION

Chapter 12, “Crafting the User Experience,” looks at a WordPress installation from the perspective of a regular or potential reader. Usability, testing, and the ease of fi nding information within a WordPress website form the basics, with added emphasis on web standards for metadata and optimization so content can be found through an appropriate Google search. Whereas Chapter 11 covers pulling external content into your WordPress instance, this chapter shows how to get your content to show up elsewhere on the Web. Alternatives for adding search functionality, one of WordPress’ weaknesses, are discussed, along with content accessibility and delivery to mobile devices. Chapter 13, “Statistics, Scalability, Security, and Spam,” deals with good and bad popularity. Keeping a WordPress installation safe from inevitable comment spammers as well as malicious attackers is a key part of confi guration and management, and this chapter covers the more popular security and anti-spam plugins and features. Traffi c analysis tools indicate how well certain content types, functions, ad campaigns, promotions, or links are driving readership and how this informs traffi c management. Chapter 14, “WordPress as a Content Management System,” goes beyond blogging to examples of WordPress as a system for managing the life cycle, integration, and distribution of networked content. Chapter 15, “WordPress in the Enterprise,” tackles issues of scale and integration. WordPress may address defi ciencies in “enterprise scale” content management tools, and building on the mechanisms covered in Chapter 12, this chapter shows how to use WordPress with a variety of enterprise facilities ranging from identity management to Microsoft ASP.NET services. Chapter 16, “WordPress Developer Community,” is an introduction to contributing to the WordPress ecosystem by working on the core, submitting plugins or themes, adding to the documentation canon, and assisting other developers. An overview of WordPress sister projects such as bbPress for forums is provided along with a brief summary of other developer resources and a glossary of WordPress-context sensitive terms.

WHAT YOU NEED TO USE THIS BOOK

You’ll need at least a rudimentary understanding of HTML and some knowledge of cascading style sheets (CSS) to make use of the theme and user experience sections of the book. Experience in writing and debugging PHP code is a prerequisite for more advanced developer sections, although if you’re just going to make changes based on the samples in this book, you can use the code as a template and learn on the fl y. A basic knowledge of databases, especially the syntax and semantics of MySQL, is in order to make the most out of the chapter on data management as well as develop plugins that need to save data. It’s helpful to have an interactive development environment in which to view PHP code, or PHP code sprinkled through HTML pages. Choosing a set of developer tools often borders on religion and deep personal preference (and we know plenty of coders who believe that vi constitutes a development environment). Some of the more user-friendly tools will make walking through the WordPress code easier if you want to see how functions used in the examples appear in the core.

xxiv

flast.indd xxiv 04/12/12 9:18 AM INTRODUCTION

Most important, if you want to use the code samples and examples in this book, you’ll need a WordPress website in which to install them. Chapter 1 covers some basic WordPress hosting options as well as the simple mechanics of downloading the components, and installing WordPress on a desktop or test machine for debugging and closer inspection. Finally, some people might argue that to really take advantage of WordPress you need to be able to write, but that ignores the basic beauty of the WordPress platform: it takes the power of the printing press to an individual level. This book isn’t about what you say (or might say); it’s about how you’re going to get those ideas onto the web and how the world will see them and interact with your blog. The source code for the samples is available for download from the Wrox website at: www.wrox.com/remtitle.cgi?isbn=9781118442272

CONVENTIONS

To help you get the most from the text and keep track of what’s happening, we’ve used a number of conventions throughout the book.

WARNING Boxes like this one hold important, not-to-be forgotten information that is directly relevant to the surrounding text.

NOTE Notes indicate notes, tips, hints, tricks, or and asides to the current discussion.

As for styles in the text: ➤ We italicize new terms and important words when we introduce them. ➤ We show fi le names, URLs, and code within the text like so: persistence.properties. ➤ We present code in two different ways:

We use a monofont type with no highlighting for most code examples.

We use bold to emphasize code that's particularly important in the present context.

SOURCE CODE

As you work through the examples in this book, you may choose either to type in all the code man- ually, or to use the source code fi les that accompany the book. All the source code used in this book is available for download at www.wrox.com. Specifi cally for this book, the code download is on the Download Code tab at: www.wrox.com/remtitle.cgi?isbn=9781118442272

xxv

flast.indd xxv 04/12/12 9:18 AM INTRODUCTION

You can also search for the book at www.wrox.com by ISBN. A complete list of code downloads for all current Wrox books is available at www.wrox.com/dynamic/books/download.aspx.

NOTE Because many books have similar titles, you may fi nd it easiest to search by ISBN, which is 978-1-118-44227-2.

At the beginning of each chapter for which there is downloadable code, we’ve provided a reminder of the URL at which you can fi nd the code fi les. Throughout each chapter, you’ll also fi nd references to the code fi le names in listing titles or the text.

Most of the code on www.wrox.com is compressed in a .ZIP, .RAR archive, or similar archive format appropriate to the platform. Once you download the code, just decompress it with your preferred compression tool.

ERRATA

We make every effort to ensure that there are no errors in the text or in the code. However, no one is perfect, and mistakes do occur. If you fi nd an error in one of our books, like a spelling mistake or faulty piece of code, we would be very grateful for your feedback. By sending in errata, you may save another reader hours of frustration, and at the same time, you will be helping us provide even higher quality information. To fi nd the errata page for this book, go to: www.wrox.com/remtitle.cgi?isbn=9781118442272 And click the Errata link. On this page you can view all errata that has been submitted for this book and posted by Wrox editors.

If you don’t spot “your” error on the Book Errata page, go to www.wrox.com/contact/techsup- port.shtml and complete the form there to send us the error you have found. We’ll check the information and, if appropriate, post a message to the book’s errata page and fi x the problem in subsequent editions of the book.

P2P.WROX.COM

For author and peer discussion, join the P2P forums at http://p2p.wrox.com. The forums are a web-based system for you to post messages relating to Wrox books and related technologies and interact with other readers and technology users. The forums offer a subscription feature to e-mail you topics of interest of your choosing when new posts are made to the forums. Wrox authors, editors, other industry experts, and your fellow readers are present on these forums.

xxvi

flast.indd xxvi 04/12/12 9:18 AM INTRODUCTION

At http://p2p.wrox.com, you will fi nd a number of different forums that will help you, not only as you read this book, but also as you develop your own applications. To join the forums, just follow these steps: 1. Go to http://p2p.wrox.com and click the Register link. 2. Read the terms of use and click Agree. 3. Complete the required information to join, as well as any optional information you wish to provide, and click Submit. 4. You will receive an e-mail with information describing how to verify your account and com- plete the joining process.

NOTE You can read messages in the forums without joining P2P, but in order to post your own messages, you must join the forum.

Once you join, you can post new messages and respond to messages other users post. You can read messages at any time on the web. If you would like to have new messages from a particular forum e-mailed to you, click the Subscribe to this Forum icon by the forum name in the forum listing. For more information about how to use the Wrox P2P, be sure to read the P2P FAQs for answers to questions about how the forum software works, as well as many common questions specifi c to P2P and Wrox books. To read the FAQs, click the FAQ link on any P2P page.

xxvii

flast.indd xxvii 04/12/12 9:18 AM flast.indd xxviii 04/12/12 9:18 AM 1 First Post

WHAT’S IN THIS CHAPTER?

➤ Appreciating the provenance of the WordPress platform ➤ Choosing a suitable platform for your WordPress installation ➤ Downloading, installing, and performing basic confi guration of WordPress ➤ Diagnosing and resolving common installation problems If displaying “Hello World” on an appropriate device defi nes minimum competence in a pro- gramming language, generating your fi rst post is the equivalent in the online publishing world. This chapter provides a brief history of WordPress and then explores several options for host- ing a WordPress installation. Common miscues and misperceptions along with their resolu- tions round out the chapter and put you on the edge of publishing your wit and wisdom. Once you’ve installed, confi gured, and completed the bare-bones administration, you’re ready to take advantage of the code walk-throughs and detailed component descriptions in later chapters. Of course, if you already have a functional WordPress website, you can skip this chapter, and dive in headfi rst to explore the core code in Chapter 2, “Code Overview.”

WHAT IS WORDPRESS?

WordPress is one of the most popular open source content management systems available, with global and vibrant user, developer, and support communities. While it can be compared to TypePad, Moveable Type, Google’s Blogger, and the Apache Roller project as a user-gener- ated content workhorse, WordPress distinguishes itself with a broad array of hosting options, functional extensions (plugins), and aesthetic designs and elements (themes).

c01.indd 1 12/6/12 1:11 AM 2 ❘ CHAPTER 1 FIRST POST

With the rise of self-publishing, low-cost web hosting, and freely available core components like the MySQL database, blogging software followed the same trend as most other digital technologies, moving from high-end, high-cost products to widely available, low-cost consumer or “hobbyist” systems. WordPress isn’t simply about creating a blog so that you can have a digital diary attached to your vanity URL; it has evolved into a full-fl edged content management system used by individuals and enterprises alike. This section takes a brief tour through the early history of WordPress and brings you up to speed on the current release and user community. WordPress started similarly to many other popular open source software packages: Some talented developers saw a need to create a powerful, simple tool based on an existing project licensed under the GPL. Michel Valdrighi’s b2/cafelog system provided the starting point, and WordPress was built as a fork of that code base by developers and . WordPress fi rst appeared in 2003 and was also built on the MySQL open source database for persisting content with PHP as the development platform. Valdrighi remains a contributor to the project, which is thriving as it has a growing and interested community of users and developers on a growing and interested community of users and developers. As with other systems written in PHP, it is self-contained in the sense that installation, confi guration, operation, and administration tasks are all contained in PHP modules. WordPress’s popularity has been driven in part by its simplicity, with the phrase “fi ve-minute installation” making appearances in nearly every description or book about WordPress. Beyond getting to a fi rst post, WordPress was designed to be extended and adaptable to the different needs of different people. WordPress today is supported by a handful of core developers and many key contributors. Mike Little runs the WordPress specialty shop zed1.com and he contributes the occasional patch to the code. Matt Mullenweg’s company, , continues to operate the .com hosting service as well as fund development of related content and site management tools, including , multi-site WordPress, and . Akismet is a robust, Automattic-hosted spam detection and protection service with a statistically (and incredibly) low failure-to-detect rate. Previously known as WordPress MU, multi-site WordPress functions are at the heart of the wordpress.com hosting system and are now merged into the main WordPress source tree. Gravatar dynamically serves images tied to e-mail addresses, providing a hosted icon with a variety of display options. Think of it as a service to make hot-linking your profi le picture technically and socially acceptable. As a content management system, the WordPress system defi nition doesn’t stop at time-serialized posts with comments. BuddyPress is a set of themes and plugins that extends WordPress into a functional social networking platform, allowing registered users to message and interact with each other, again with all content managed within the WordPress framework. Similarly, bbPress is a PHP- and MySQL-based system designed for forums (bulletin boards) that is distinct from WordPress but is commonly integrated with it. Chapter 16, “WordPress Developer Community,” covers some of the WordPress adjunct systems in more detail, but they’re included here to provide a sense of how WordPress has expanded beyond a basic single-user–oriented tool. At the same time, the authors are not endorsing or making a commercial for Automattic, but delving into the guts of WordPress without a spin of the propeller hat toward Mullenweg and Little is somewhere between incorrigible and bad community behavior.

c01.indd 2 12/6/12 1:11 AM Popularity of WordPress ❘ 3

POPULARITY OF WORDPRESS

This book is based on the WordPress 3.5 major release. Each successive release of WordPress has included improvements in the administration and control functions (Dashboard); backup, export, and import functions; and installation and upgrade features. Even if you start with a slightly down-rev version of WordPress, you’ll be able to bring it up to the current release and maintain the freshness of your install. Install and upgrade paths are touched on later in this chapter. But just how popular is WordPress?

Current State Interest in WordPress and WordPress usage is booming. You’re holding in your hands a testament to that. Just 3 years ago, very few WordPress books were available. Now this second edition has been published. “Popular” is always a subjective metric, but statistics add some weight to those perceptions. According to Automattic, as of 2011, over 100,000 new WordPresses are created every day (http://en.wordpress.com/stats/). That includes sites using WordPress for content management, blogging, and personal rants, and has to be discounted by those of you who have multiple WordPress installations to their names, but even with that order of magnitude estimate, WordPress is immensely popular. Automattic cites nearly 74 million WordPress websites globally with about half of them hosted at WordPress.com (http://en.wordpress.com/stats/). In the previous edition of this book, that number was at only 5 million sites. In 2008, the offi cial WordPress plugin repository hosted over 6,300 plugins, double the number from 2007. At the time of this writing, the number of plugins now tops 19,000 (http://wordpress.org/news/2012/05/ plugins-refreshed/). There are over 1,500 unique themes in the offi cial WordPress theme repository, which does not include all the commercial theme vendors and independent developers creating their own custom themes. The combinations of plugins and themes require scientifi c notation to represent in complexity, but at the same time, they’re all equally simple to locate, integrate, and use. That’s the result of a solid architecture and an equally solid community using it. In short, the ecosystem surrounding WordPress is alive and thriving. In August 2011, Matt Mullenweg presented the current state of WordPress use as well as results from the fi rst-ever WordPress survey at the San Francisco WordCamp. The WordPress survey is similar to a census of the WordPress community at large and how people use WordPress every day. This includes independent web developers, corporate users, and hobbyists providing a great cross-section of the larger WordPress population. The following highlights demonstrate how active and prevalent WordPress is on the global Internet: ➤ Nearly 15 percent of the top 1 million visited websites use WordPress. ➤ On average, 22 of every 100 new websites run WordPress. ➤ More than 200 million plugins have been downloaded from the plugin repository. ➤ 18,000 individuals responded to the survey representing over 170,000 websites. Mullenweg’s State of the Word keynote can be seen at WordPress.tv.

c01.indd 3 12/6/12 1:11 AM 4 ❘ CHAPTER 1 FIRST POST

Today, WordPress powers many large media companies’ websites or portions thereof, including CNN’s blogs, the Wall Street Journal’s All Things , Reuters, Forbes, and the irreverent but snowclone-driven icanhazcheeseburger.com. (If you looked for a back story on “snowclone,” apologies, but that’s also the joy of discovering new facts in a culture of participatory media.) WordPress is used by Fortune 500 companies such as GM, UPS, and Sony. WordPress is a viable choice for a range of users, from international conglomerates to major recording artists to huge media publishing companies.” Some need reassurance before choosing WordPress and focus on which big boys are using it, you can fi nd a list online at the WordPress Notable Users showcase (http://en.wordpress.com/notable-users/). But the simplicity, ease of use, and ultimately the power of the plugins and themes make WordPress suitable for your mom’s family information website, your local elementary school teacher’s class- room newsletter, and the hobbyist. These are truly some of the WordPress success stories of today and these widely accessible, more narrowly popular websites are what makes WordPress popular. WordPress is adaptable and will be as simple or complex as you need it to be. Empowering “lower tech” users to be web publishers and then spreading the word (pun intended) to their family and friends about how easy WordPress is to use have fueled this explosive growth and adoption.

Where do you get started? wordpress.org is the home for the current released and in-development versions of the code. Click through to wordpress.org/extend for a starting point in fi nding plugins, themes, and wish lists of ideas and features to be implemented.

wordpress.com has both free and paid hosting services. Over at wordpress.org/hosting you’ll fi nd a list of hosting providers that support WordPress and often include some additional fi rst-time installation and confi guration support in their packaging of the code for delivery as part of their hosting services.

Intersecting the Community WordPress thrives and grows based on community contributions in addition to sheer usage. Like high school gym class, participation is the name of the game, and several semi-formal avenues along which to channel your efforts and energies are available. WordCamp events are community-hosted and locally operated, and now happen in dozens of cities around the world. Offi cial WordCamps are listed on wordcamp.org, but you’ll do just as well to search for a WordCamp event in a major city close to you. WordCamps occur nearly every weekend with bloggers, photographers, writers, editors, developers, and designers of all experience and skill levels counted among their attendees. WordCamps are a low-cost introduction to the local community and often a good opportunity to meet WordPress celebrities. Visit wordcamp.org to fi nd the next WordCamp. Less structured but more frequently convened than WordCamps are WordPress Meetups, compris- ing local users and developers in nearly 200 (up from the 40 mentioned in the fi rst edition of this book) cities. You’ll need a meetup.com account, but once you’re registered, you can check on loca- tions and timetables at wordpress.meetup.com to see when and where people are talking about content management.

A rich, multi-language documentation repository is hosted at codex.wordpress.org. The WordPress Codex, with all due respect to the term reserved for ancient handwritten manuscripts,

c01.indd 4 12/6/12 1:11 AM Popularity of WordPress ❘ 5

represents the community-contributed tips and tricks for every facet of WordPress, from installation to debugging. If you feel the urge to contribute to the WordPress documentation, register and then write to your heart’s content in the WordPress Codex. Hopefully you’ll fi nd this book a cross between a companion and a travel guide to the Codex. Finally, mailing lists (and their archives) exist for various WordPress contributors and communities. A current roster is available online at codex.wordpress.org/Mailing_Lists; of particular interest may be the wp-docs list for Codex contributors and the wp-hackers list for those who work on the WordPress core and steer its future directions.

WordPress and the GPL WordPress is licensed under the Gnu Public License (GPL) version 2, contained in the license.txt fi le that you’ll fi nd in the top-level code distribution. Most people don’t read the license and simply understand that WordPress is an open source project; however, pockets of corporate legal departments still worry about the viral component of a GPL license and its implications for additional code or content that gets added to, used with, or layered on top of the original distribution. Much of this confusion stems from liberal use of the words “free” and “copyright” in contexts where they are inappropriately applied. The authors of this book are not lawyers — nor do they play them on the Internet or on television — and if you really want to understand the nuances of copyright law and what constitutes a “conveyance” of code, pick up some of Lawrence Lessig’s or Cory Doctorow’s work in those areas. This section is included to minimize the concerns of IT departments who may be dissuaded from using WordPress as an enterprise content management system by overly zealous legal teams. Don’t let this happen to you; again, if WordPress is acceptable to CNN and the Wall Street Journal, two companies that survive on the copyrights granted to their content, it probably fi ts within the legal strictures of most corporate users as well. The core tenet of the GPL ensures that you can always get the source code for any distribution of GPL-licensed software. If a company modifi es a GPL-licensed software package and then redistributes that newer version, it has to make the source code available as well. This is the “viral” nature of GPL at work; its goal is to make sure that access to the software and its derivatives is never reduced in scope. If you plan on modifying the WordPress core and then distributing that code, you’ll need to make sure your changes are covered by the GPL and that the code is available in source code form. Given that WordPress is written in PHP, an interpreted language, distributing the software and distributing the source code are effectively the same action. Following are some common misperceptions and associated explanations about using WordPress in commercial situations. ➤ “Free software” means you can’t commercialize its use. You can charge people to use your installation of WordPress, or make money from advertisements running in your website, or use a WordPress content management platform as the foundation of an online store. That’s how wordpress.com works; it also enables Google to charge advertisers for using their -based services. You can fi nd professional quality WordPress themes with non-trivial price tags, or you can pay a hosting provider hundreds or even thousands of dollars a year to run your MySQL, PHP, Apache, and WordPress software stack; both involve commercialization of WordPress.

c01.indd 5 12/6/12 1:11 AM 6 ❘ CHAPTER 1 FIRST POST

➤ If you customize the code to handle your own {content types, security policies, obscure navigational requirements} you’ll have to publish those changes. You’re only required to make the source code available for software that you distribute. If you choose to make those changes inside your company, you don’t have to redistribute them. On the other hand, if you’ve made some improvements to the WordPress core, the entire community would benefi t from them. Getting more staid employers to understand the value of community contribution and relax copyright and employee contribution rules is sometimes a bit challenging, but the fact that you had a solid starting point is proof that other employers made precisely that set of choices on behalf of the greater WordPress community. ➤ The GPL will “infect” content that you put into WordPress. Content — including graphic elements of themes, posts, and pages managed by WordPress — is separated out from the WordPress core. It’s managed by the software, but not a derivative of or part of the soft- ware. Themes, however, are a derivative of the WordPress code and therefore also fall under the GPL, requiring you to make the source code for the theme available. Note that you can still charge for the theme if you want to make it commercially available. Again, the key point here is that you make the source code available to anyone who uses the software. If you’re going to charge for the use of a theme, you need to make the source code available under the GPL as well, but as pointed out previously, users installing the theme effectively get the source code. More important than a WordPress history lesson and licensing examination are the issues of what you can do with WordPress and why you’d want to enjoy its robustness. The next section looks at WordPress as a full-fl edged content management system, rather than simply a blog editing tool.

CONTENT AND CONVERSATION

Multiple linear feet of shelves in bookstores are fi lled with volumes that will improve your writing voice, literary style, blogging techniques, and other aspects of your content creation abilities. One of the goals of this book is to defi ne the visual, stylistic, and context management mechanisms you can build with WordPress to shape vibrant user communities around your content. That context stimulates conversation with your readers. It’s not just about the words in each post, or even if you’re an interesting writer. How will people fi nd you? How will you stand out in the crowd? How do you put your own imprint on your site, and personalize it for whatever purpose: personal, enterprise, community, or commercial?

WordPress as a Content Management System Blogging systems have their roots in simple content management operations: Create a post, persist it in stable storage such as a fi lesystem or database, and display the formatted output based on some set of temporal or keyword criteria. As the richness and types of content presented in blog pages expanded, and the requirements for sorting, searching, selecting, and presenting content grew to include metadata and content taxonomies, the line between vanilla, single-user–targeted blogging software and enterprise-grade content management systems blurred. Content management systems (CMS) handle the creation, storage, retrieval, description or annotation, and publication or display of a variety of content types. CMS also covers workfl ow

c01.indd 6 12/6/12 1:11 AM Content and Conversation ❘ 7

tasks, typically from an editorial or publishing perspective, but also including actions such as approval and marking content for additional editing or review. The WordPress Dashboard provides those elements of workfl ow management and editorial control. WordPress isn’t the only open source content management system in widespread use today; the Drupal and Joomla projects are equally popular choices. Drupal and Joomla start from the perspective of managing content repositories; they handle a variety of content types, multiple authors in multiple roles, and delivering the content to a consumer that requests it. WordPress is at its heart a blogging system, and the end focus is on displaying content to a reader. Although areas of functional overlap exist, you can integrate WordPress with other content management systems, a process covered in detail in Chapter 14. WordPress has established itself as a bona fi de content management system through its design for extensibility and the separation of content persistence from content display. Taking some liberties with the Model-View-Controller design pattern, WordPress separates the MySQL persistence layer as a data model, the theme-driven user interface and display functions, and the plugin architecture that interposes functionality into the data to presentation fl ow. Most important, WordPress stores content in raw form, as input by the user or an application posting through the WordPress APIs. Content is not formatted, run through templates, or laid out until the page is rendered, yielding immense power to the functions that generate the actual HTML. At the same time, the data model used by WordPress uses a rich set of tables to manage categories (taxonomies), content tags (folk- sonomies), author information, comments, and other pieces of cross-reference value. The WordPress database schema that makes this possible is explored in Chapter 6. Although that design gives WordPress incredible power and fl exibility as a content management system, it also requires knowledge of how those data persistence and control fl ows are related (it was a search for such a dissection of WordPress in functional terms that got us together to write this book).

Creating Conversation

“Conversation is king; content is just something to talk about.” — Cory Doctorow

A robust CMS is measured by the utility of its content. Even the richest content types and most well- managed processes are of low return if nobody actually consumes the outputs. It’s not suffi cient to install blogging software, write a few posts, and hope the world shows up on your virtual doorstep; you need to create what Tim O’Reilly calls an “architecture of participation.” Social networking, advertising, feeds, and taking steps to ensure your site shows up in search engine results will drive readers to your site; the design, branding, and graphic elements coupled with the quality of your content will encourage them to take the steps toward active participation. Look at the problem from the perspective of a reader: in a world of tens of millions of websites (many of which have a “fi rst post” and not much else) how will you be found, heard, and echoed? Your Twitter followers should want to read your site, and your WordPress site can update your Twitter feed. Conversely, your Twitter updates may appear in your WordPress sidebar, marrying the ultra-short content timeline to the more thoughtful one. If you’re active on Facebook, you can import entries into a public fi gure page and Facebook readership will drive traffi c back to your

c01.indd 7 12/6/12 1:11 AM 8 ❘ CHAPTER 1 FIRST POST

website. If you cover specifi c, detailed, or arcane areas in your writing, Google searches for those terms should direct readers to you, where they’ll join the conversation. Chapter 11, “Content Aggregation,” covers getting content into WordPress from social media and other content systems, and Chapter 12, “Crafting a User Experience,” looks at how your WordPress content can be more broadly distributed.

GETTING STARTED

Before any serious work on presentation, style, or content begins, you need a home for your website (despite the previous discussion about WordPress and content management systems, you’ll refer to your website and the actual WordPress installation that implements it interchangeably, mostly for convenience and brevity). Factors affecting your choice include: ➤ Cost — Free hosting services limit your options as a developer, and frequently preclude you from generating money from advertising services. More expensive offerings may include better support, higher storage or bandwidth limits, or multiple database instances for additional applications. ➤ Control — What tools are provided for you to manage your MySQL database, fi les com- prising the WordPress installation, and other content types? If you want to be able to muck around at the SQL level, or manage MySQL through a command-line interface, you should ensure your hosting provider supports those interfaces. ➤ Complexity — You can install the Apache web server with a PHP interpreter, MySQL, and the WordPress distribution yourself, but most hosting providers have wrapped up the installation process so that some of the rough edges are hidden from view. If you expect to need technical support on the underlying operating system platform, fi nd a provider (including your own IT department) that provides that support in a reasonable time . This section takes a quick look at some hosting options, walks you through the basics of a do-it- yourself installation, and concludes with an overview of the ways in which WordPress and MySQL choose to ignore each other when installation goes into the weeds.

Hosting Options Three broad categories of WordPress hosting exist, each with trade-offs between administrative complexity and depth of control. The easiest and most popular is to use wordpress.com, a free host- ing service run by Automattic using the multi-site version of WordPress (originally WordPress MU). You can install themes and plugins through the Dashboard but you can only enable or disable the choices that come preinstalled. Further, you won’t have access to the underlying MySQL databases and core code, or be able to integrate WordPress with other systems. You can redirect one of your own URLs to wordpress.com, but if you want full control over everything from the code to the URLs used, you’re probably looking at a paid option. The free route may be a reasonable fi rst step for you, but here it is assumed that you’re going to want to perform surgery on your installation.

You’ll fi nd a starter list of for-fee hosting providers on www.wordpress.org, including the paid option on wordpress.com. Most have the latest, or close to latest, releases of the WordPress core

c01.indd 8 12/6/12 1:11 AM Getting Started ❘ 9

available as a package to be installed in conjunction with MySQL and a web server. The third host- ing option is to install everything on servers that you own and operate. If your servers live in a host- ing facility but you enjoy root administrative access, that’s equivalent to a do-it-yourself installation. WordPress requires a web server with PHP support, a URL rewriting facility, and an instance of MySQL. Apache is the most popular option for front-ending WordPress because it provides PHP interpretation through mod_php and URL rewriting in mod_rewrite. There is growing interest in lighttpd (Lighty) as a replacement for Apache, although the URL rewriting functionality needs a bit of hand-holding. Finally, you can use Microsoft’s IIS 7.0 as a web server with its URL_rewrite module. The emphasis on URL rewriting stems from WordPress’s support for “pretty” permalinks to blog entries, allowing you to create a URL tree organized by date, category, tag, or other metadata. Those mnemonic or human-readable URLs are converted into MySQL database queries to extract the right WordPress content based on titles or other keywords as part of the WordPress main loop, which is covered in detail in Chapter 5. Your web server decides whether the URL should be parsed by WordPress or if it refers to a specifi c HTML fi le based on what’s in the .htaccess fi le, and the URL rewriting rules assure that its contents are interpreted properly. Technically, URL rewriting isn’t required to install WordPress, but it’s good to have because it gives you tremendous fl exibility in the presentation and naming conventions used for your content’s URLs. Permalink design and practices are covered in more detail in Chapter 2, but keep the requirement in mind as you select your WordPress substrate. Up to this point, MySQL has been mentioned only in passing, but a brief review of MySQL requirements rounds out the hosting prerequisite list. It’s worth establishing some terminology and distinguishing between the MySQL software, database instances, and WordPress instances using MySQL. When you install and confi gure MySQL, you have a full-fl edged relational database system up and running. It doesn’t have to be confi gured on the same machine as your web server, and some hosting providers will create horizontally scalable MySQL “farms” in parallel to their web server front ends. An instance of MySQL running on a server can support multiple databases, each with a unique name. When you install WordPress, you’ll need to know the name of the MySQL database reserved for your content, although this information may be auto-generated and confi gured for you if you’re using a provider that supports WordPress and MySQL as an integrated package. WordPress creates a number of relational data tables in that named database for each website that you create. Confusion can result from nomenclature and complexity. You (or your hosting provider) may run multiple MySQL instances on multiple servers, and you’ll need to know where your database is hosted. Because each instance of MySQL can run multiple databases, and each database contains groups of tables, it’s possible to run multiple MySQL-based applications on the same hosting platform, using one MySQL instance or even one MySQL database. If you want to have multiple WordPress sites on the same server, you can share a single MySQL database instance for all of them provided you confi gure WordPress to distinguish the MySQL database table names within the MySQL database. It’s a simple confi guration option that is covered in the next section, and it highlights the distinction between multiple sets of tables in a database and multiple databases for distinct applications. Once you’ve secured the necessary foundation, it’s time to get the code up and running. Even if you’re using a hosting provider that installs MySQL and WordPress for you, it’s worth knowing how the server-side components interact in case you need to track down a problem when you’re deep in plugin development.

c01.indd 9 12/6/12 1:11 AM 10 ❘ CHAPTER 1 FIRST POST

Do It Yourself Installation The famous, fabled, fabulous fi ve-minute WordPress installation is a reality when everything is con- fi gured and coordinated properly. This section walks you through the steps that are often hidden from view when you use a provider with packaged installs, and highlights some of the common mis- fi res between WordPress and MySQL instances. The installation process is quite simple (assuming that your web server and MySQL server are already running): download the WordPress package and install it in your web server’s directory tree, and then navigate to your top-level URL and complete the confi guration. One (compound) sentence describes it completely. It’s possible and even advisable to install a fully functioning WordPress instance on your laptop or development machine, particularly if you are going to be working on the core, developing plugins or otherwise making changes that would create embarrassing failures during testing on a public website. Mac OS X comes with an Apache web server (with PHP and URL rewriting); download MySQL from www..com, or use a prepackaged confi guration such as MAMP (www..info, which includes the phpMyAdmin tool), and you’ll have a self-contained development and deploy- ment lab. For other platforms, XAMPP (www.apachefriends.org) has a neatly integrated platform stack that runs on Windows, Mac OS and Linux foundations. Having everything under one hood is a powerful option for examining failure modes, as you’ll see in the next two sections. More infor- mation on working with WordPress locally is covered in Chapter 3.

Installing WordPress Files If you download the WordPress code from wordpress.org, you’ll get a zip (or tarball) archive that expands into a directory called wordpress. The fi rst part of a WordPress installation is to get the code into your web server’s directory structure; ensuring you have it in the right place is a critical step. Gloss over this part and you’ll fi nd your website ends up with a URL like http://example.com/ wordpress and you’ll either have to start over or e-mail ugly URLs to your friends and family. If that’s what you want — to distinguish your WordPress site from other content on your website or to isolate multiple sections — choosing the fi lesystem layout is equally important. Pick the top-level directory where you want to install WordPress. Most commonly, this is the root directory for your web server, and if you’re using a hosting provider it’s probably the subdirectory called public_html in the fi le tree. If you are using a packaged install where there’s a menu asking you for the target location, make sure you pick this top-level directory (and yes, you know that it already exists, that’s the point!); if you’re copying fi les from your local machine to the web server target using an FTP client, make sure you pick the right destination. The somewhat obvious move to copy the zip fi le to the server and then unpack it will put everything into a wordpress subdirectory, and if you want your WordPress site’s URL to be http://example.com rather than http://example .com/wordpress, move the fi les up one directory level before proceeding. There is a confi guration option to have your WordPress installation in a subdirectory to your top-level URL, so it’s not fatal if you drop WordPress into a less-than-desirable fi lesystem geography. That is covered at the end of this section. Once the WordPress fi les are installed, your fi lesystem browser should show you something like Figure 1-1, with an index.php and template wp-config-sample.php fi le. That’s the entirety of the WordPress system, which runs effectively within the web server’s PHP interpreter.

c01.indd 10 12/6/12 1:11 AM Getting Started ❘ 11

FIGURE 1-1: A clean but unconfi gured WordPress installation

At this point, if you’re doing a manual installation, you’ll want to create your own wp-config.php fi le by editing the sample provided and saving it in your top-level WordPress directory. As an alternative, you can navigate to your website’s URL, and the WordPress code will notice there’s no confi guration fi le and present you with dialog boxes like those in Figures 1-2 and 1-3 where you can fi ll in the details. You’ll need the MySQL database name, database username, and some idea of the WordPress database table prefi x (other than the default wp_). These lower-level details are the guts of the next section on database confi guration. If you are using a hosting provider with packaged installations, you probably won’t see this step because the WordPress fi les will be extracted and the MySQL database information will be automatically inserted into a confi guration fi le, no end user–serviceable parts inside.

FIGURE 1-2: WordPress will create a new wp-confi g fi le if one does not exist.

c01.indd 11 12/6/12 1:11 AM 12 ❘ CHAPTER 1 FIRST POST

FIGURE 1-3: Database confi guration dialog box

What do you do if you already have HTML or other content at your target URL and you want to add WordPress to an existing site? Disposition of existing fi les depends on your desired fi rst user experience upon navigating to your URL. To use WordPress as a content management system as described here, your best choice is to save existing content and convert it into new posts or pages, effectively making your previous site color commentary and context for your WordPress-driven site. Alternatively, you can install WordPress in a subdirectory, keep your existing index. fi le, and direct readers to your new content through a button or link on your extant home page. Don’t leave this to chance; if you have an index.html fi le and then install WordPress, you’ll have an index.php and an index.html fi le side by side and users will see one or the other depending upon the Directory Index confi guration of your site’s web server. Actions on existing content should be informed by how much traffi c that content is driving to your site: if your pages are responsible for search engine traffi c, you probably don’t want to disrupt the existing URLs that have been cached and should install WordPress in a subdirectory. If you feel strongly about making WordPress the wrapper around the user experience, move the content and include URL rewriting or redirection for pages that move into the WordPress world.

If you used a hosting provider’s packaged installation, or if you manually created a wp-config .php fi le and then navigated to your top-level URL, WordPress should have completed creating the database tables, created an administrative user for your WordPress, and set an initial password, as shown in Figure 1-4. Make sure you change the username to something different than admin.

c01.indd 12 12/6/12 1:11 AM Getting Started ❘ 13

FIGURE 1-4: Complete website details and set up admin user

Upon a successful installation, you should see a box like Figure 1-5 that indicates your fi ve minutes of famed installation are done.

FIGURE 1-5: Administrative information at the conclusion of a clean install

c01.indd 13 12/6/12 1:11 AM 14 ❘ CHAPTER 1 FIRST POST

The next section covers the MySQL-WordPress confi guration dance in more detail and is suitable reading even if thinking about SQL gives you hives. If you’re up and running, you can skip the next section and go right to the section, “Finishing Up.”

Database Confi guration If your hosting provider spun up a MySQL database and created a user for you, check your resultant wp-config.php fi le to gather this information. It is necessary for the MySQL probing covered in this section, and it’s good to have in case you run into MySQL problems later on. There’s a user- name and password combination included in that fi le, so treat it the way you’d treat other login information. On the other hand, if you’re going deep on the do-it-yourself route, this section gives you a sense of what’s likely to create confusion or consternation as you pull the pieces together. In theory, MySQL setup for WordPress is trivial: make sure MySQL is up and running, create a WordPress user in MySQL, and then have that user create a database to hold the WordPress tables. You can use the MySQL command line or tools such as phpMyAdmin or Chive for these tasks, but bear in mind that MySQL has its own set of users and permissions granted to those users, distinct from those used by your (or your hosting provider’s) operating system. Once MySQL is installed, it will create a default table of users and grants, adding a root user on Unix systems that is a MySQL superuser, unrelated to the Unix root user. However, if you’re attempting to connect to your MySQL instance as the MySQL root user, those connections can only be made from localhost — the same machine on which MySQL is running. If you want to learn more about MySQL permissions, the table governing grants of those permissions to users, and how MySQL users are managed, refer to the “MySQL Reference Manual” (http://dev.mysql.com/doc/) and the sections on securing the initial MySQL accounts. No set naming conventions exist for WordPress users or databases; hosting providers will typically append the name of the package or your account information to distinguish users that benefi t from MySQL database co-tenancy. Again, it’s possible to have multiple databases, owned by the same user or different MySQL users, running in a single MySQL database server instance. In the example shown in Figure 1-3, wp_ is used as a prefi x for both usernames and database names, at least provid- ing a hint to the database administrator that these belong to a WordPress installation. What can go wrong between WordPress and MySQL? The following are the three primary root causes of installation failure. Note that all of these conditions need to be fulfi lled at installation time; there has to be some basic database structure to contain the admin user before you can log in as that admin. 1. Web server can’t fi nd MySQL. Either you have the hostname for the MySQL server noted incorrectly in the wp-config.php fi le, or the web server is looking for a local MySQL instance and can’t open the socket connection to it. Here’s a simple example: when you run WordPress locally on Mac OS, MySQL creates the socket /tmp/mysql.sock for local connections, but the WordPress PHP code is going to look for /var/mysql/mysql.sock through the PHP engine’s MySQL module. Simply symbolically link one to the other: # ln -s /tmp/mysql.sock /var/mysql/mysql.sock

The actual fi lesystem path to the local MySQL socket is a function of the database confi guration; when it starts up, it creates the local socket. Where the PHP engine, and therefore any PHP- based applications, looks for this socket is PHP confi guration dependent. If you want to figure out exactly where the mismatch is, a bit of heavy-handed printf() style debugging helps.

c01.indd 14 12/6/12 1:11 AM Getting Started ❘ 15

Edit wp-includes/wp-db.php, the set of functions that establish WordPress’s database connection. If you’re seeing the “Error establishing a database connection” message during installation, insert an echo(mysql_error()); statement where the error is detected to see the details displayed along with the generic message, as shown in Figure 1-6: if (!$this->dbh) { echo(mysql_error()); $this->bail(sprintf(/*WP_I18N_DB_CONN_ERROR*/"

Error establishing a database connection

FIGURE 1-6: mysql_error( ) reporting a socket problem

The mysql_error() function is a PHP library function that spits out the error generated by the last MySQL function called. 2. WordPress fi nds MySQL but can’t log in. Most of the time, the MySQL username or pass- word is wrong, particularly when you have to copy some arbitrary username generated by a hosting provider. Double-check your username data, and verify that it is refl ected properly in your wp-config.php fi le. You may also run into a password authentication issue when using MySQL 4.1 or MySQL 5.0 with some web servers’ PHP implementations; they only support the older MySQL 4.0 password hashing scheme. If this is the case, use MySQL’s OLD_PASSWORD() function to hash your WordPress user’s password in the backward-com- patible format; the magic SQL incantation (at the MySQL command-line prompt or within the SQL window of MAMP) to address the following: SET PASSWORD FOR user@host = OLD_PASSWORD('password');

In this instance, user@host is your WordPress database username and database hostname, and password is the (clear text) password you provided in the confi guration fi le. 3. WordPress connects to MySQL but can’t select the database. Just because the web server can log in to the database server with your WordPress database user information doesn’t mean that there’s necessarily a database available to that user. This is another scenario best

c01.indd 15 12/6/12 1:11 AM 16 ❘ CHAPTER 1 FIRST POST

diagnosed with mysql_error(), by inserting it in wp-db.php where the selection error is identifi ed: function select($db) { if (!@mysql_select_db($db, $this->dbh)) { $this->ready = false; echo(mysql_error()); $this->bail(sprintf(/*WP_I18N_DB_SELECT_DB*/' ...

Can’t select database

..

If, after inserting the mysql_error() statement as described earlier, your attempts to com- plete installation result in an error box like that shown in Figure 1-7, your MySQL database wasn’t created under the appropriate database user, or the database user doesn’t have privi- leges to use it. Double-check what MySQL believes using the following command line: % /usr/local/mysql/bin/mysql -u wp_user1 -p Enter password: Welcome to the MySQL monitor. Commands end with; or \g. Your MySQL connection id is 174 Server version: 5.1.37 MySQL Community Server (GPL) mysql> show databases; +——————————+ | Database | +——————————+ | information_schema | | test | +——————————+ 2 rows in set (0.00 sec)

FIGURE 1-7: MySQL database selection error

Once you logged in as your designated MySQL database user, you didn’t see the MySQL database — in this case, it was probably created by MySQL user root, and permissions to access or modify it weren’t granted to the WordPress installation’s MySQL user. If you have

c01.indd 16 12/6/12 1:11 AM Finishing Up ❘ 17

MySQL root access, or suffi cient MySQL user privileges to create new databases within the MySQL instance, it’s easy enough to create a database once logged in on the command line:

mysql> create database wp_halstern; Query OK, 1 row affected (0.00 sec)

Again, it’s important to distinguish operating system users from MySQL users from WordPress users. MySQL users are defi ned in the database and granted privileges to create databases, muck with tables, and otherwise generate useful data. WordPress users exist within the WordPress database tables created during install; they only have privilege, context, and meaning once you’re logged in to WordPress. Once you have a clean WordPress installation, you should see a collection of tables named according to the table prefi x you set in wp-config.php; again, this is easy enough to verify using the MySQL command line: mysql> use wp_halstern; show tables; Database changed +—————————————+ | Tables_in_wp_halstern | +—————————————+ | wp_hs_comments | | wp_hs_links | | wp_hs_options | | wp_hs_postmeta | | wp_hs_posts | | wp_hs_term_relationships | | wp_hs_term_taxonomy | | wp_hs_terms | | wp_hs_usermeta | | wp_hs_users | +—————————————+ 10 rows in set (0.00 sec)

In this example, you set the database table prefi x to wp_hs_; if you later add another WordPress installation using the same database user and instance, you can simply set a different prefi x and have the two sites co-mingled in the same database table. You dig into the schema and uses of the ten basic WordPress database tables in Chapter 6. For now, once you are happily connected to MySQL, you’re ready for some fi nal clean-up and fi rst-time administration.

FINISHING UP

At this point, your MySQL database is up and running. There’s a home for your content, and your web server is happily executing the WordPress core code. There are just a couple more things to discuss.

First-Time Administration Once you have completed the installation, proceed to log in with the credentials you set up in Figure 1-4 and you’ll see the basic WordPress Dashboard captured in Figure 1-8.

c01.indd 17 12/6/12 1:11 AM 18 ❘ CHAPTER 1 FIRST POST

FIGURE 1-8: Dashboard view upon a fi rst-time login

If you’re not redirected to the Dashboard through the Log In button, or if you happen to visit your website’s top-level URL fi rst, either click the Log In link on your website or explicitly go to the wp-admin subdirectory (example.com/wp-admin) to be presented with a login dialog box. Logging in to your website takes you to the WordPress Dashboard, which is both amazingly simple in its power and rich in its complexity and exposed features. What you do next with the Dashboard depends on how happy you are with the basic installation. If, as in the preceding example, you ended up with an older version of WordPress, click the Update button to do an in-place upgrade to the latest distribution. In addition to having a strong self-installation feature, WordPress includes self-update functions (in wp-admin/includes/update .php if you’re looking for them). You may decide to change some basic confi guration options, such as the database name or the MySQL database user, although you’ll only change the default of root@localhost if you have full control over the web and database servers. The confi guration fi le also has entries for “security keys” that are used to provide stronger security for browser cookies. Security keys are discussed in more detail in Chapter 11. Editing your wp-config.php fi le affects the changes right away. Changing the database table prefi x, for example, causes WordPress to instantiate a new set of tables and create a clean-slate installation. Make those edits and then go back to your top-level URL and you’ll fi nd yourself with new admin user information and logged in to a starter Dashboard, as in Figure 1-8. Old tables aren’t removed from MySQL, so you’ll have to do manual cleanup. At this point, if you want to set your URL to be different from the location in which you installed WordPress, you can choose Settings and General from the Dashboard and change the URLs for

c01.indd 18 12/6/12 1:11 AM Finishing Up ❘ 19

both your top-level address as well as the WordPress installation directory. If you dissociate your site’s URL and the WordPress directory, make sure you move the index.php fi le to the desired top-level URL, and then edit the last line to include the proper subdirectory path to WordPress. Before creating your fi rst post, it’s also a good idea to establish a permalink structure so that everything you write follows the naming conventions you’ve chosen to make it relatively easy for readers to fi nd, share, and link to your content. As expected, it’s another option in the Settings portion of the Dashboard; options for permalink naming and their impact on performance and database schema are covered in more detail in the next chapter. Whether it’s really been fi ve minutes, or a few hours of tracking down mismatches in hostnames, usernames, and database confi gurations, you’re now ready to publish the fi rst post of your own writing.

First Post A successful WordPress installation already has a fi rst post and comment published, thus assuring that all of the moving pieces are moving in unison, and giving your website some initial content. When you’re ready to add your own fi rst words, either use the right-hand QuickPress panel in the Dashboard to post an entry (you may need to dismiss the new website help fi rst), or go to Posts and click Add New to be taken to the built-in WordPress editor. Figure 1-9 shows an entry in progress in the QuickPress panel, followed by the updated Dashboard after it’s been successfully posted.

FIGURE 1-9: Publishing from the QuickPress panel

c01.indd 19 12/6/12 1:11 AM 20 ❘ CHAPTER 1 FIRST POST

If your tastes run more old-school, you can always crank out content in your favorite and then copy it into the editing pane. Be careful with WYSYIWIG word processors such as Microsoft Word or OpenOffi ce if you want to copy into the WordPress HTML composition window because the HTML will be riddled with additional tag and style information. Finally, a variety of standalone editors, such as Illumnix’s Ecto, publish to WordPress using the Atom Publishing Protocol or XML-RPC. Options for enabling posts to be published remotely are, as you’d expect, in the Dashboard’s Settings section under Writing options. Click Publish for your own “Hello World” moment. Multiple subsystems created that editing pane, saved the content in a database, generated and saved the referential metadata, and then emitted nice-looking HTML. Most of the user-visible pieces are governed through the Dashboard and certain functions will be covered in various chapters.

SUMMARY

This chapter covered how WordPress got to where it is today, with a brief history lesson and also touching on its current popularity. Part of WordPress’s rise in the web realm is attributed to the simplicity of the installation process. The next chapter dives into the core of WordPress so that you can take advantage of its extensibility, friendly design, and function.

c01.indd 20 12/6/12 1:11 AM 2 Code Overview

WHAT’S IN THIS CHAPTER?

➤ Downloading WordPress ➤ Confi guring wp-config.php and .htaccess ➤ Exploring the wp-content directories ➤ Enabling maintenance mode in WordPress

WordPress is a software package that comprises groups of source code fi les that perform specifi c tasks within the system. Understanding the code, including fi le and folder structure, is essential to understanding how WordPress works as a whole. After reading this chapter, you will be familiar with downloading and exploring the WordPress fi lesystem. This chapter also discusses confi guring key WordPress fi les, including the powerful wp-config.php and .htaccess fi les. It also covers some advanced confi guration options available in WordPress.

DOWNLOADING

The fi rst step to installing WordPress on your own hosting account is to download the source fi les required for WordPress to run. This section digs deeper into the core of WordPress.

Download Locations You can download the latest stable release of WordPress directly from WordPress.org by visiting the download page located at http://wordpress.org/download/.

c02.indd 21 12/6/12 1:12 AM 22 ❘ CHAPTER 2 CODE OVERVIEW

You can also update WordPress directly from your current WordPress installation by visiting the Updates WordPress section under the Dashboard ➪ Updates SubPanel. Click the Download button to download the latest version of WordPress to your computer. WordPress also features Subversion (SVN) access. Subversion is a free, open source version control system. WordPress uses Subversion to manage fi les and directories and the changes made to them. You can download the latest WordPress source code by checking out http://core.svn .wordpress.org/trunk/. The SVN trunk directory contains the bleeding edge version of WordPress that is actively being developed. Typically, this version of WordPress contains bugs and is generally used for testing purposes. Running a production website using the trunk version of WordPress is not recommended. SVN is the mechanism developers use to actively develop on the WordPress core software. With SVN, you can create and submit patch fi les for inclusion into the WordPress core. Chapter 16 covers this in detail.

Available Formats The default format for the WordPress software download is in a compressed zip archive named latest.zip. You can also download WordPress in a compressed tar archive named latest.tar.gz. There is no difference between the fi les in the archive, only the compression method used. You can download the zip and tar archives directly from these URLs:

➤ http://wordpress.org/latest.zip

➤ http://wordpress.org/latest.tar.gz

These download links never change. Each new version of WordPress is automatically compressed and saved at this location when the version is tagged. When you save the archive to your computer, you should rename the fi le to include the WordPress version number, such as wordpress-3.5.zip. This will help you remember what version of WordPress you saved to your computer.

Release Archive WordPress.org features a Release Archive for WordPress. The Release Archive features a list of downloadable archives for every release of WordPress since version 0.71. The archive is located at http://wordpress.org/download/release-archive/. Remember that only the most current version of WordPress is actively maintained so these downloads are more for reference than actual use. “Actively maintained” means that critical fi xes for security, performance, or reliability problems are made to the active branch and not applied retroactively to previous releases. If you need the fi x, you’ll need to upgrade your installed version of WordPress. Another great use for these older versions of WordPress is to roll a website back to a previous ver- sion. For example, if you update a very old version of WordPress to the latest stable version and run into problems, you could easily download the old version that the website was originally running to revert to. The Release Archive also features a download for every beta and release candidate version of WordPress as well. This is great to see the overall growth of WordPress as a software platform.

c02.indd 22 12/6/12 1:12 AM Directory and File Structure ❘ 23

The release archives are also useful if you need to update an old version of WordPress that has hacks made to the core. Simply compare the website’s WordPress source code with the same version of WordPress from the release archive and any differences, or core hacks, will be discovered.

DIRECTORY AND FILE STRUCTURE

The WordPress source code features many different PHP, JavaScript, and CSS code fi les. Each fi le serves a specifi c purpose in WordPress. The beauty of open source software is that all code is pub- licly available, which means you can easily explore the code to better understand how WordPress functions. The best resource for learning WordPress is the WordPress software itself. After extracting the WordPress download, you will notice the set fi le structure for WordPress, as shown in Figure 2-1. WordPress comes with three directories by default: wp-admin, wp-content, and wp-includes. Core fi les are all fi les in the wp-admin and wp-includes directories and the majority of the fi les in the root WordPress directory. The wp-content directory holds all of your custom fi les, including themes, plugins, and media. This directory contains the code that controls content manipulation and pre- sentation in WordPress. WordPress HTML content, such as pages and posts, is stored in the MySQL database along with metadata such as tag and category structures, both of which are covered in detail in Chapter 6. FIGURE 2-1: Default WordPress fi le and folder Modifying any of the core WordPress fi les can structure result in an unstable website. An innocuous but badly executed change to the Dashboard or login functions, for example, will leave you with a WordPress installation that can’t be managed. Core changes also make it very diffi cult to update WordPress because all changes made are overwritten when the updated version of WordPress is installed. As discussed in the previous section, critical fi xes to the WordPress core are only made in the current branch, so if you are forced to update WordPress to pick up a security fi x, you’re going to have to re-integrate any core changes you’ve made and hope they don’t confl ict with the changes you want. Maintaining the integrity and stability of your WordPress installation over time is much simpler when you’re not changing fi les in the core.

In general, the wp-admin, wp-includes, and root directory core WordPress fi les should never be edited, but the next section covers some core root directory fi les that can be modifi ed as part of advanced confi guration. In general, however, follow this rule that is revisited in Chapter 4: Don’t hack the core!

c02.indd 23 12/6/12 1:12 AM 24 ❘ CHAPTER 2 CODE OVERVIEW

WORDPRESS CONFIGURATION

WordPress features specifi c fi les that can be edited for different purposes. These fi les can alter how WordPress functions. Always test changes in a development environment before publishing to a production server. This section covers database connections, storing FTP info, enabling debugging tools, and more using wp-config.php. It also covers the power of the .htaccess fi le, including increasing PHP memory limits and max upload sizes, creating redirects, and setting access restrictions.

wp-confi g.php File The most important fi le in any WordPress installation is the wp-config.php fi le. This fi le con- tains all database connection settings, including the database name, username, and password to access your MySQL database. This fi le also stores additional database and other advanced set- tings. The wp-config.php fi le was originally named wp-config-sample.php. Renaming the fi le to wp-config.php is one of the fi rst steps to installing WordPress.

The wp-config fi le is typically stored in the root directory of WordPress. Alternatively, you can move the wp-config fi le out of the WordPress root directory and into the parent directory. So if your WordPress directory is located here,

/public_html/my_website/wp-config.php

you can safely move the fi le to here:

/public_html/wp-config.php

WordPress looks for the wp-config fi le in the root directory fi rst, and if it can’t fi nd that fi le it looks in the parent directory. This happens automatically so no settings need to be changed for this to work.

NOTE Moving the wp-config.php out of the root WordPress directory is a good security measure, making it nearly impossible to potentially access this fi le from a web browser.

Some options in WordPress are stored as constants and these can be seen in the wp-config.php fi le. The constraints all have the same format:

define( 'OPTION_NAME', 'value' );

OPTION_NAME is the name of the option constant being set; value is the option value and can be updated to whatever setting you would like to save for that option. When adding new options to the wp-config.php fi le, it’s important the options are added above the line that reads:

/* That's all, stop editing! Happy blogging. */

c02.indd 24 12/6/12 1:12 AM WordPress Confi guration ❘ 25

If your WordPress installation is having problems connecting to your database, this is the fi rst place to start troubleshooting. If you receive the error message “Error establishing a database connection,” the fi rst thing to do is verify that the DB_NAME, DB_USER, and DB_PASSWORD options are correctly set for your database server. Also verify that the DB_HOST name is set to the correct host for your server. Typically, this is set to localhost, but some hosting companies confi gure WordPress packages with web servers and MySQL servers on different machines, necessitating a host company–specifi c con- fi guration option to locate the MySQL database. Contact your hosting tech support or consult their online documentation for the correct host value to set here.

You can change the database character set (charset) by changing the DB_CHARSET option value. By default, this is set to utf8 (Unicode UTF-8), which supports any language, and is almost always the best option.

Since WordPress 2.2, the DB_COLLATE option has allowed designation of the database collation, that is, sort order of the character set. (A character set is a collection of symbols that represents words in a language. The collation determines the order to use when sorting the character set, usually alphabetical order.) This option, by default, is blank and should typically stay that way. If you would like to change the database collation, just add the appropriate value for your language. You should change this option before installing WordPress. Altering this value after installation could cause problems in WordPress.

WordPress security can be strengthened by setting secret keys in your wp-config.php fi le. A secret key is a hashing salt, which makes your site harder to hack by adding random elements (the salt) to the password you set. These keys aren’t required for WordPress to function, but they add an extra layer of security on your website. To have secret keys auto-generated for you, visit the link to WordPress.org for secret key genera- tion in your wp-config.php fi le (https://api.wordpress.org/secret-key/1.1/salt/), shown in Figure 2-2. Alternatively you can just type a bunch of random characters in place of “put your unique phrase here.” The goal is to use secret keys that are 100 percent random and unique.

FIGURE 2-2: Randomly generated secret keys

You can add or change these keys at any time; the only thing that will happen is all current WordPress cookies will be invalidated and your users will be required to log in again.

Another security feature included in wp-config.php is the ability to defi ne the database table prefi x for WordPress. By default this option value is set to wp_. You can change this value by setting the $table_prefix variable value to any prefi x, like so:

$table_prefix = 'bwar_';

c02.indd 25 12/6/12 1:12 AM 26 ❘ CHAPTER 2 CODE OVERVIEW

If a hacker is able to exploit your website using an SQL injection attack, this will make it harder for them to guess your table names and quite possibly keep them from doing SQL injection at all. Setting the table prefi x to a unique value also makes it possible to run multiple WordPress installations in a single database. If you want to change the table prefi x after you have installed WordPress, you can use the WP Security Scan plugin (http://wordpress.org/extend/plugins/wp-security-scan/) to do so. Make sure you make a good backup before doing this, however.

The wp-config fi le also contains the option for localizing your installation of WordPress. WordPress has the built-in capability to be used in many different languages. Setting the WPLANG option value sets the default language for WordPress to use. A corresponding MO (machine object) fi le for the selected language must be installed to wp-content/languages for this option to work. MO fi les are compressed PO (portable object) fi les, which contain translations for WordPress messages and text strings in a specifi c language. The MO and PO fi les are components of the GNU “gettext” subsystem that underlies the WordPress multilanguage capabilities. For a full list of avail- able MO language fi les, visit the following resources:

➤ WordPress in Your Language Codex page — http://codex.wordpress.org/ WordPress_in_Your_Language

➤ WordPress Language File Repository — http://svn.automattic.com/wordpress-i18n/

Debugging errors in WordPress can be made easier using the WP_DEBUG option. Enabling WP_DEBUG displays WordPress errors on the screen, rather than suppressing those errors with a white screen. To enable WP_DEBUG, just set the option value to true:

define( 'WP_DEBUG', true );

New installations of WordPress will have this option defi ned in wp-config as false. If this option is not defi ned, it defaults to false and error messages are not displayed. Remember to disable or remove this option when you are done debugging because error messages might help hackers fi nd vulnerabilities in your website. It’s best to always keep WP_DEBUG enabled when developing in WordPress to address any warnings or errors that might be displayed.

Advanced wp-confi g Options You can set additional advanced options in your wp-config fi le. These options are not in the wp-config fi le by default so you will need to manually add them to the fi le. To set your WordPress address and blog address, use the following two options:

define( 'WP_SITEURL', 'http://example.com/wordpress' ); define( 'WP_HOME', 'http://example.com/wordpress' );

The WP_SITEURL option allows you to temporarily change the WordPress site URL. This does not alter the database option value for siteurl, but instead temporarily changes the value. If this option is removed, WordPress reverts back to using the siteurl database setting. The WP_HOME option works the exact same way, letting you temporarily change the home value for WordPress. Both values should include the full URL including http://.

c02.indd 26 12/6/12 1:12 AM WordPress Confi guration ❘ 27

NOTE This is a useful technique if you are building a WordPress website under a temporary development URL, such as new.example.com. You can simply remove these two options when you go live and WordPress will load using the production URL instead.

Version 2.6 introduced an option that allows you to move the wp-content directory. The two required options are:

define( 'WP_CONTENT_DIR', $_SERVER['DOCUMENT_ROOT'] . '/wordpress/blog/wp-content' ); define( 'WP_CONTENT_URL', 'http://domain.com/wordpress/blog/wp-content');

The WP_CONTENT_DIR option value is the full local path to your wp-content directory. The WP_CONTENT_URL is the full URI of this directory. Optionally, you can set the path to your plugins directory like so:

define( 'WP_PLUGIN_DIR', $_SERVER['DOCUMENT_ROOT'] . '/blog/wp-content/plugins' ); define( 'WP_PLUGIN_URL', 'http://example/blog/wp-content/plugins');

WP_PLUGIN_DIR and WP_PLUGIN_URL are options used by plugin developers to determine where your plugin folder resides. If a plugin developer is not using these constants, there is a very good chance their plugin will break if you move your wp-content directory. Never move the wp-content directory on your production server without fi rst testing in a development environment. WordPress saves post revisions for each saved edit made to a post or page. Edits are saved by click- ing either the Save or Publish button, and also by the built-in auto-save feature of WordPress. Imagine if each post you create has 10 revisions. If you had 100 posts, that would be 1,000 records in your database. This can quickly increase the size of your database and may even slow down your website because table records can take longer to fetch in larger databases. Luckily, WordPress has a built-in post revisions option called WP_POST_REVISIONS. You can set this option to false to com- pletely disable post revisions altogether, or you can specify a maximum number of revisions to keep for each post or page. Following are examples of both scenarios:

define( 'WP_POST_REVISIONS', false ); define( 'WP_POST_REVISIONS', 5 );

You can also confi gure the auto-save interval by setting the AUTOSAVE_INTERVAL option. WordPress uses when editing a post to auto-save revisions. By default, this interval is 60 seconds. You can set the interval in seconds for auto-save in wp-config. Set auto-save to 5 minutes by using this code:

define( 'AUTOSAVE_INTERVAL', 300 );

A great debugging option is SAVEQUERIES. Activating this option saves all database queries into a global array that can be displayed on your page. This can help you debug query issues, and also to

c02.indd 27 12/6/12 1:12 AM 28 ❘ CHAPTER 2 CODE OVERVIEW

see exactly what WordPress is executing on each page load. If you are working on a theme or plugin, and can’t seem to get the right set of posts back, this debug option will show you exactly what WordPress is asking for out of the database. Enable this option by setting the value to true:

define( 'SAVEQUERIES', true );

To display the query array in your theme, add the following code to any theme template fi le to view:

if ( current_user_can( 'manage_options' ) ) { global $wpdb; print_r( $wpdb->queries ); }

The preceding code displays the saved query array only if the logged-in user has the ability to man- age options, essentially locking it down so only site administrators will see the output. Themes and template fi les are covered in Chapter 9, “Theme Development.”

You can also enable logging directly from your wp-config fi le. To enable logging, fi rst you need to create a php_error.log fi le and upload it to your root WordPress directory. Then simply turn on the log_errors PHP option and point to your logging fi le:

@ini_set( 'log_errors','On' ); @ini_set( 'display_errors','Off' ); @ini_set( 'error_log','/public_html/wordpress/php_error.log' );

All errors will now be logged to this fi le. This will also log any errors produced by enabling the WP_DEBUG option discussed earlier. In the preceding example display_errors is set to Off, which is perfect for a production website because you don’t want error messages displayed. If you are debug- ging and want to view errors in real time, just set that option to On. Remember the error_log value is relative to the web server’s document root, not the WordPress root.

You can also set the memory limit WordPress is allowed to use with the WP_MEMORY_LIMIT option. If your website hits the memory limit set for WordPress to run, you will see the error “Allowed memory size of xxxxx bytes exhausted.” Increasing the memory limit fi xes this problem. The memory limit is set by defi ning the megabytes needed:

define( 'WP_MEMORY_LIMIT', '32M' );

Setting this option works only if your hosting company allows it. Some hosting companies will not allow you to dynamically change the memory limit and will have this value set very low. This problem is usually found on lower-cost hosting companies that maintain their price points by packing more web server instances onto a single physical host, creating contention for memory footprint. This increases the memory only for WordPress and not other applications running on your server. To increase the memory limit across all of your websites, set the php_value memory_limit variable in your php.ini fi le. For example, when importing large amounts of content, say months or years worth of blog posts, it’s likely you’ll hit this memory limit.

c02.indd 28 12/6/12 1:12 AM WordPress Confi guration ❘ 29

One amazing feature of WordPress is the built-in localizer. WordPress displays in English by default, but can easily be set to display any language that has been translated. Setting the WPLANG option triggers WordPress to load the specifi ed language fi les:

define ( 'WPLANG', 'en-GB' );

The option value shown previously comprises the ISO-639 language code followed by the ISO-3166 country code. So en-GB would be English-Great Britain. This setting will reference your .mo and .po fi les for language translation.

You can also defi ne the LANGDIR option. This option defi nes what directory will hold your language .mo fi les. By default, WordPress looks in wp-content/languages for the .mo fi le. If you would like to move this folder, just set the LANGDIR option like so:

define( 'LANGDIR', '/wp-content/bury/my/languages' );

WordPress will now look in the new location for your .mo fi les.

CUSTOM_USER_TABLE and CUSTOM_USER_META_TABLE are also very powerful options. They are useful if you want to have two or more individual WordPress installs use the same user accounts. Remember to set this prior to installing WordPress.

define( 'CUSTOM_USER_TABLE', 'joined_users' ); define( 'CUSTOM_USER_META_TABLE', 'joined_usermeta' );

Setting these two options enables you to defi ne the name of the default WordPress user and user meta table. Doing this means both websites share user information including usernames, passwords, author bios, and so on. This is a great way to set up a new installation of WordPress but not lose sync with your current user base. If you would like your users to have different roles on each WordPress install, but still share user accounts, don’t set the CUSTOM_USER_META_TABLE option. Everything stored in the user tables will stay the same, but everything else will be blog-specifi c (that is, user level, fi rst and last name, and so on).

You can set multiple cookie options such as COOKIE_DOMAIN, COOKIEPATH, and SITECOOKIEPATH. These options are typically used in a WordPress Multisite installation utilizing subdomains for websites. This allows you to set the primary domain so cookies can be created and validated on all subdomains in the network.

define( 'COOKIE_DOMAIN', '.domain.com' ); define( 'COOKIEPATH', '/' ); define( 'SITECOOKIEPATH', '/' );

Typically, you won’t need to use or change this option, but if you run into issues with cookies, this is the fi rst place to check. Since the inclusion of the automatic installer functionality for plugins and themes, as well as the automatic update process, you can set FTP settings directly in your wp-config fi le. This is only needed if your host is not confi gured to support the automatic install process. This is easily detect- able because each time you try to install a plugin or theme you are asked for your FTP information.

c02.indd 29 12/6/12 1:12 AM 30 ❘ CHAPTER 2 CODE OVERVIEW

To save your FTP information in WordPress, add the following options in your wp-config fi le:

define( 'FTP_USER', 'username' ); define( 'FTP_PASS', 'password' ); define( 'FTP_HOST', 'ftp.example.com:21' );

Just enter your FTP username, password, and host with port and you’re all set! WordPress will no longer ask for your FTP information when using the automatic installer. You can set additional FTP/SSH options for various confi gurations:

// sets the filesystem method: "direct", "ssh", "ftpext", or "ftpsockets" define( 'FS_METHOD', 'ftpext' ); // absolute path to root installation directory define( 'FTP_BASE', '/public_html/wordpress/' ); // absolute path to wp-content directory define( 'FTP_CONTENT_DIR', '/public_html/wordpress/wp-content/' ); // absolute path to wp-plugins directory define( 'FTP_PLUGIN_DIR ', '/ public_html /wordpress/wp-content/plugins/' ); // absolute path to your SSH public key define( 'FTP_PUBKEY', '/home/username/.ssh/id_rsa.pub' ); // absolute path to your SSH private key define( 'FTP_PRIVKEY', '/home/username/.ssh/id_rsa' ); // secure FTP SSL-connection if supported by the hosting company define( 'FTP_SSL', false );

You can also override default fi le permissions in WordPress using the FS_CHMOD_FILE and FS_CHMOD_DIR options:

define( 'FS_CHMOD_FILE',0644 ); define( 'FS_CHMOD_DIR',0755 );

The numeric single digit values represent the User, Group, and World permissions set for fi les and folders on your web server. To learn more about WordPress and fi le permissions visit http://codex.wordpress.org/Changing_File_Permissions. These settings can help with certain hosting companies that use restrictive permissions for all user fi les. This will override the server settings and should allow WordPress updates and auto installations to work.

The WP_CACHE option is required for some caching plugins to work. Enabling this option will include the fi le wp-content/advanced-cache.php. To enable this option use the following code:

define( 'WP_CACHE', true );

WordPress has numerous constant options that you can set. There is a PHP function to view all constants currently set on your installation:

print_r( @get_defined_constants() );

An advanced option is forcing SSL on login to your WordPress site. This requires users to log in via the HTTPS access link and encrypts all data being transferred to and from your website. To activate SSL on login, add the FORCE_SSL_LOGIN option like so:

c02.indd 30 12/6/12 1:12 AM WordPress Confi guration ❘ 31

define( 'FORCE_SSL_LOGIN', true );

You can also force all admin pages to use SSL. This is activated with the FORCE_SSL_ADMIN option, like so:

define( 'FORCE_SSL_ADMIN', true );

This forces all admin dashboard pages (/wp-admin) to be encrypted with SSL. Keep in mind that activating this setting slows down your admin page load times, but all data passed to and from WordPress will be encrypted using SSL. Also remember that your website must be confi gured to work with SSL. The quick way to test is to visit your site using https, as in https://example.com. If the page loads, SSL is set up on your server. Forcing SSL on the admin side of WordPress is a great security enhancement. All data passed to and from WordPress will be encrypted, preventing someone from potentially stealing your WordPress login credentials. Since version 2.9, WordPress has featured a trash bin. This trash bin contains any posts, pages, attachments, and comments that have been deleted. This allows you to recover any content that you might have accidently deleted in WordPress. By default, the trash bin is emptied every 30 days. Emptying the trash bin will permanent delete any items in the trash. You can modify this interval by setting the EMPTY_TRASH_DAYS option like so:

define( 'EMPTY_TRASH_DAYS', 7 );

The trash will now automatically be emptied every 7 days. You can also disable the trash completely by setting the option value to 0. The trash link will now be replaced with a Delete Permanently link. Keep in mind that WordPress will not ask for a confi rmation when you click Delete Permanently. There is also an option to disable WordPress cron. Cron is used to execute scheduled tasks in WordPress. Some common schedule tasks include posting a scheduled post and checking for new versions of WordPress, themes, and plugins. To disable WordPress cron add this option to your wp-config fi le:

define( 'DISABLE_WP_CRON', true );

This section covered a lot of common options for wp-config. There are many more, less common, options for wp-config available in WordPress. A great resource for learning about wp-config options is the Codex: http://codex.wordpress.org/Editing_wp-config.php.

.htaccess The .htaccess fi le is used primarily for creating pretty permalinks and keyword injected URLs for your website. WordPress by default creates ugly query-string formed URLs, usually with an ID present, such as http://example.com/?p=45. These URLs are completely functional but aren’t very friendly to search engines and site visitors. By enabling pretty permalinks, WordPress creates URLs based on site content, such as post and page titles, category and tag names, and dates for archives.

c02.indd 31 12/6/12 1:12 AM 32 ❘ CHAPTER 2 CODE OVERVIEW

Enabling Permalinks To enable permalinks visit the Settings ➪ Permalinks SubPanel on your WordPress Dashboard, as shown in Figure 2-3. Select any permalink structure other than Default and click the Save Changes link.

Upon saving your changes, WordPress tries to create your default .htaccess fi le. If your root WordPress directory is writable by the server, the fi le is created automatically. If WordPress is unable to create the .htaccess fi le, you will see instructions on how to manually create the file, as shown in Figure 2-4.

FIGURE 2-4: Manual info for FIGURE 2-3: Enabling permalinks in WordPress creating the .htaccess fi le

Setting a permalink structure using the month and year like this,

/%year%/%monthnum%/%postname%/

creates a permalink like this:

http://example.com/2012/10/halloween-party/

Using permalinks offers many advantages, such as: ➤ Search Engine Optimization (SEO) — Keywords in your URL can give your website a big SEO boost. Search engines will use these keywords in their algorithm for positioning in their search results. ➤ Forward compatibility — Regardless of what platform your website uses (WordPress, Drupal, Joomla!), having a solid permalink structure can be easily replicated should you ever migrate. ➤ Usability — Visitor-unfriendly ID URLs make it equally unpleasant to share a link with a friend. It’s diffi cult to differentiate the content between your ID-driven URLs.

c02.indd 32 12/6/12 1:12 AM WordPress Confi guration ❘ 33

➤ Sharing — In this Internet era of social networking, sharing is a natural extension of your online presence. Keywords in the URL would make fi nding your link extremely easy and convey an immediate context for the content.

.htaccess Rewriting Rules Usually a web server takes a URL that references a fi le in the server’s document fi lesystem, loads that fi le, and processes the content in it to generate HTML sent back to the user’s browser. For WordPress fi les such as wp-login.php, that’s exactly how the login screen is generated. When presented with a pretty permalink such as example.com/2012/travel/haddonfield, the web server just needs to load the main loop of WordPress so that the core code can parse the URL and turn it into a database query that fi nds a post with the title Haddonfi eld in the category Travel. Unlike a static website where you would have created a fi le with that name, WordPress stores its content in a database — only a few fi les are loaded directly. The “secret sauce” behind the WordPress permalink mechanism is summarized in three rewriting rules added to the .htaccess fi le when you enable permalinks:

RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [ L]

Quite simply, these rules check the URL used to access your site to see if it refers to an existing fi le or directory in the fi lesystem hierarchy. The !-f and !-d notations are negations; .htaccess is ensuring that the URL does not refer to any valid fi le or directory pathname. If the URL does, in fact, match a valid fi le — for example, a WordPress administrative function such as wp-login .php — then no rewriting is done and the web server tries loading that fi le (to execute the PHP code contained within). If there’s no fi le or directory at the path specifi ed by the supplied URL, then the incoming URL is rewritten to index.php, invoking the core of the WordPress system. You’ll dig into the steps used to convert a URL string into a MySQL query in a bit more detail as a preface to the discussion of the content display loop in Chapter 5.

NOTE The simple check for whether a fi le or directory exists can have unin- tended side effects if you put non-WordPress web server content in the same directory structure as the WordPress code. For example, consider a directory of images as a peer directory of wp-content: example.com/wp-content and example.com/images. You might choose to bypass the WordPress media library because those images are managed by their own set of ingest processes. What happens when a user forms a URL with a mistyped image name that points to a nonexistent fi le? The .htaccess rewriting rule will fi re because there is no fi le with that name, and the WordPress core will be started. A user expecting to see an image will instead get the default WordPress site content when they should have received a 404 error for a nonexistent URL target. If you are going to add directories around your WordPress installation, either place WordPress in its

continues

c02.indd 33 12/6/12 1:12 AM 34 ❘ CHAPTER 2 CODE OVERVIEW

(continued) own subdirectory (example.com/wordpress) or add a rewrite rule to .htaccess that recognizes your added peer directories and immediately hands those URLs off to the web server:

RewriteRule ^images/(.*) images/$1 [L]

This rule effectively says, “Take any URL that starts with the component images, and pass it off to the web server.” The [L] directive means “stop processing after matching this rule,” and the rewrite itself simply echoes back what it was passed. If you’re going to have a few directories sitting in parallel with the WordPress installation, you’ll need one rewrite rule for each.

The .htaccess fi le can also manage URL redirects. If you change your About page from http:// example.com/about to http://example.com/about-me, anyone who visits your original URL will hit a 404 page. A URL redirect will redirect from the old URL to the new URL so your visitors won’t get lost. This also alerts search engines about the new URL so they can update their index. Following is an example of a 301 permanent redirect to a static page:

redirect 301 /about http://example.com/about-me

WordPress does some additional rewriting and cleanup of URLs to improve search engine results, as you’ll see in Chapter 5.

Confi guration Control Through .htaccess The .htaccess fi le is very powerful and can control more than just URL structure. For instance, you can control PHP confi guration options using the .htaccess fi le. To increase the memory allotted to PHP use this command:

php_value memory_limit 64M

This increases the memory limit in PHP to 64 MB. You can also increase the max fi le size upload and post size:

php_value upload_max_filesize 20M php_value post_max_size 20M

Now the maximum fi le size you can post from a form and upload is set to 20 MB. Most hosting companies set these values to around 2 MB by default so these are settings that will be used often for larger fi le uploads. Not all hosting companies will allow these values to be set in your .htaccess fi le, and they could create an error on your website if that is the case.

c02.indd 34 12/6/12 1:12 AM WordPress Confi guration ❘ 35

The .htaccess fi le can also be used for security purposes. Using .htaccess allows you to restrict access to your website by IP address, essentially locking it down from anonymous visitors. To lock down your website by IP addresses, add the following code to your .htaccess fi le:

AuthUserFile /dev/null AuthGroupFile /dev/null AuthName "Access Control" AuthType Basic order deny,allow deny from all #IP address to whitelist allow from xxx.xxx.xxx.xxx

Replace xxx.xxx.xxx.xxx with any IP address that you want to grant access to your website. You can have multiple allow from lines so add as many IP addresses as you need. This allows access to your website only if you are using an IP address defi ned here.

A more widely used option is to lock down your wp-admin directory. This means that only IP addresses you specify can access your admin dashboard URLs. This makes it much harder for any- one else to try to hack your WordPress back end. To accomplish this, create a separate .htaccess fi le in your wp-admin directory with the preceding code. Remember that most ISPs assign client addresses dynamically so the IP address of the computer you are using will change on occasion. If you get locked out, just update your .htaccess fi le with your new IP address or delete the fi le altogether. This is not a good tip if you allow open registrations on your website because you need to allow your users access to the wp-admin directory.

You can also allow wildcard IP addresses. For example, 123.123.123.* would allow access to any- one who matches the fi rst three IP address octets, with the fi nal digit being a wildcard. You can also allow a range of IP address. For example 123.123.123.110-230 would allow anyone with an IP address between 123.123.123.110 and 123.123.123.230.

You can also enable error logging from the .htaccess fi le. The fi rst step is to create a php-errors .log fi le in your WordPress root directory. Then add the following code to your .htaccess fi le to enable error logging:

php_flag display_startup_errors off php_flag display_errors off php_flag html_errors off php_flag log_errors on php_value error_log /public_html/php-errors.log

This enables error logging but suppresses any error messages from displaying. Again this is a perfect setup for a production environment because you don’t want errors publicly displayed.

The .maintenance File WordPress has a built-in maintenance mode that can be enabled by the .maintenance fi le. The.maintenance fi le is used by WordPress during the auto-update process. This prevents visitors

c02.indd 35 12/6/12 1:12 AM 36 ❘ CHAPTER 2 CODE OVERVIEW

from seeing any error messages as WordPress core fi les are updated. To test this feature, simply cre- ate a new .maintenance fi le and add the following line of code:

Add this fi le to your WordPress root directory and your website will instantly enter maintenance mode. This locks down your website for all visitors and displays a generic maintenance message “Briefl y unavailable for scheduled maintenance. Check back in a minute.” The time() function can be replaced with any UNIX-formatted timestamp.

You can set a custom maintenance page by creating a maintenance.php fi le and placing it in your wp-content directory. WordPress uses this fi le to display during any forced maintenance periods that you set. This allows you to create a custom maintenance notice to your website visitors.

This fi le is also used by the WordPress automatic update process. A .maintenance fi le is created right before WordPress installs the new core fi les during an update. This ensures there are never any error messages for your visitors during this process.

WP-CONTENT USER PLAYGROUND

The wp-content directory stores just about every fi le for customizing WordPress. This directory stores your plugins, themes, and additional fi les to extend WordPress in any way imaginable.

The wp-content directory has a single PHP fi le, index.php. The contents of this fi le are shown here:

So what’s the point of this fi le? Actually this is a very important file. The index.php fi le blocks any- one from viewing a directory listing of your wp-contents folder. If the index.php fi le didn’t exist, and your web server allowed directory listings, visiting http://example.com/wp-contents would display all of the fi les and folders in that directory. This can help hackers gain access to key fi les that might help exploit your website; for example if a vulnerability were discovered in a plugin, being able to view the list of directories in the WordPress plugin directory would quickly and easily inform an attacker if your site was a viable target.

If you are manually updating WordPress, make sure you avoid overwriting your wp-content directory.

Plugins Plugins are stored in the wp-content/plugins directory. A plugin can be a single fi le or multiple fi les inside of a folder. Any fi les inside the /plugins directory are scanned by WordPress to determine if the fi le is a properly formatted WordPress plugin. If the fi le is determined to be a plugin, it appears under the Plugins ➪ Installed Plugins SubPanel on your admin dashboard ready to be activated.

c02.indd 36 12/6/12 1:12 AM wp-content User Playground ❘ 37

NOTE Remember that to automatically deactivate a plugin, you can remove it from your /plugins folder. If an active plugin’s fi les are missing, WordPress deactivates the plugin before trying to render your website.

Your wp-content directory might also include a /mu-plugins directory. Must-use (mu) plugins are plugins that are automatically enabled in WordPress. Any plugins that exist in this folder will be executed just like a standard activated plugin. The major difference is mu-plugins cannot exist in a subdirectory or they will be ignored. To learn more about mu-plugins visit http://codex .wordpress.org/Must_Use_Plugins. You’ll be revisiting plugins in Chapter 8, “Plugin Development.”

Themes Themes are stored in the wp-content/themes directory. Each theme must exist in its own subdirec- tory and must consist of the proper template fi les for WordPress to recognize it as a usable theme. At a minimum, an index.php and a style.css fi le must exist in the theme directory, along with proper tagging to display under the Appearance ➪ Themes SubPanel on your admin dashboard. WordPress can store as many themes in this directory as your server allows. You can easily view a preview of any theme, or activate a new theme, under the Appearance ➪ Themes SubPanel. You’ll cover themes in much more detail in Chapter 9.

Uploads and Media Directory WordPress stores uploaded media in the wp-content/uploads folder. This directory does not exist in a default installation of WordPress. The /uploads directory is created the fi rst time you success- fully upload a fi le to WordPress. By default WordPress stores uploads in month- and year-based folders. So your uploaded image would be stored like so:

/wp-content/uploads/2012/06/image.png

Before you can upload any images or fi les in WordPress, you need to set the /wp-content direc- tory to be writable. When you upload your fi rst image, WordPress auto-creates the/uploads directory and any needed subdirectories. After you have successfully uploaded your fi rst image, reset the /wp-content permissions to not be writable, typically 755. Currently, there is no way to import images uploaded via FTP into the WordPress Media Library. If making the uploads direc- tory writeable is not an option, there are plugins available (such as NextGen Gallery, described in detail in the Custom Directories section that follows) that include this functionality. WordPress Multisite stores upload media in a different manner. Instead of one uploads directory, Multisite creates a blogs.dir directory. Inside this folder are multiple subdirectories named with an ID. This ID is the blog ID the folder is attached to. Every site in a Multisite network has a unique

c02.indd 37 12/6/12 1:12 AM 38 ❘ CHAPTER 2 CODE OVERVIEW

blog ID. You’ll cover this in more detail in Chapter 10. For example, your fi rst WordPress Multisite site upload directory would look like this:

/blogs.dir/1/files/

This helps keep individual site uploads separated and easier to maintain.

Upgrade Directory The wp-content/upgrade directory is automatically created by WordPress when you use the auto- matic update process. This folder is used by WordPress to store the new version of WordPress that is downloaded from WordPress.org. The compressed WordPress download is extracted in this folder prior to the update. This folder should remain untouched for automatic updates to process success- fully. If this directory is deleted, WordPress re-creates it the next time you run the auto-updater.

Custom Directories Some plugins that require a lot of custom fi les will store those fi les in a directory in your wp-content folders.

The Super Cache plugin (http://wordpress.org/extend/plugins/wp-super-cache/) creates a / wp-content/cache directory to store all of the cached pages created for your website. A cached page is simply a fully generated page on your website saved as a static HTML fi le. Instead of generating the page each time a user clicks one of your links, the cache plugin serves up the static HTML fi le to the visitor. This dramatically decreases WordPress load times and increases performance because pages aren’t generated on each view, but rather only when the cache is regenerated based on your settings.

The Super Cache plugin also adds two fi les to yourwp-content directory: advanced-cache.php and wp-cache-config.php. These two fi les are required for Super Cache to function correctly. When Super Cache is activated, it tries to create these two fi les. If it fails, a notice appears alerting you of this. The fi les exist in the Super Cache plugin directory and can be manually moved to the wp-content directory.

The most popular image gallery plugin, NextGen Gallery (http://wordpress.org/extend/ plugins/nextgen-gallery/), creates a /wp-content/gallery directory to store all of the images uploaded to your NextGen image galleries. Each gallery created is a subdirectory under /gallery. This helps keep your gallery image fi les very organized and easy to work with.

The WP-DB Backup plugin (http://wordpress.org/extend/plugins/wp-db-backup/) creates a / wp-content/backup-b158b folder (where b158b is a random string) to store local backups of your database. When you select the Save to Server option, all database backup fi les will be stored in this directory. It’s important to not delete your backups unless you are sure they are not needed anymore.

c02.indd 38 12/6/12 1:12 AM Summary ❘ 39

SUMMARY

In this chapter you covered downloading WordPress. You also covered confi guring key WordPress core fi les, wp-config.php and .htaccess, along with more advanced confi gurations for each. You also reviewed the wp-content directory and how WordPress interacts with custom directories. With that structural and confi guration view of WordPress, it’s time to learn how to create a local development environment so that you can begin customization and development without impacting a public site.

c02.indd 39 12/6/12 1:12 AM c02.indd 40 12/6/12 1:12 AM 3 Working with WordPress Locally

WHAT’S IN THIS CHAPTER?

➤ Developing locally ➤ Getting started with a local development environment ➤ Confi guring a local development environment — tips and tricks ➤ Moving your local project to production

Now that you know how to obtain WordPress as well as what the basic lay of the land looks like, let’s take a look at how to get started doing something with WordPress, something beyond simply using WordPress as a website engine. Any user can install WordPress and use it to power a website, as you saw in Chapter 1, which is one of the reasons why WordPress has been so successful. As a developer, however, you need a full-featured but sandboxed place to experiment, try out new ideas, and fi gure out what has failed, without taking down a production or public site. As the fi rst step in building something, to take WordPress to the next step in your own projects, let’s look at the benefi ts of setting up a local development environment on your workstation or laptop. This chapter starts with a brief swing outside the realm of WordPress to talk about general software development.

BENEFITS OF WORKING LOCALLY

Developing locally is considered a best practice. In general, you do not want to be actively developing on a live production website because you could have visitors accessing the site at any time and development involves iterations of breaking code and making it work again. This is not the experience you want to provide to your visitors.

c03.indd 41 12/6/12 1:14 AM 42 ❘ CHAPTER 3 WORKING WITH WORDPRESS LOCALLY

What is “developing locally?” In short, it means you have a full WordPress installation to which you can make changes, add new code, and fail with impunity. It’s a sandbox, and it’s the fi rst element in a successful deployment cycle.

Typical Deployment Cycle Before diving into the reasons to develop locally fi rst, you’ll explain the different phases of deployment. Deployment involves taking your code from the base development versions that you feel are now ready for the world through staging and testing to a production website. In general, there are three levels. Some workfl ows will have more, but these three steps are the essentials: development, staging, and production. This is a basic software development workfl ow and applies to more than just WordPress development. First is the development environment, where you do all of your day-to-day work. As you’ll see in this chapter, this is typically your local workstation or laptop, but in some scenarios it might be a development location on a remote server. While it is best practice to develop your solution on a platform that is the same type of system as the production environment, this is not always practical. For example, your production web servers are high-end server class hardware running Linux, but because your developers need access to corporate resources such as Microsoft Exchange, they run Windows workstations for development. This is why the second tier is introduced, which is the staging or testing environment. After the developer has tested his solution on his development environment, he prepares to deploy it on a staging server. The intention of the staging server is to bridge the gap between the development environment and the target production environment without the risk of breaking the live website. As you will see later in this chapter, there are variances you have to consider when developing cross-platform code — that is, code that can run on Windows, Mac OS X, or Linux. This staging environment gives the developer an opportunity to make sure his code will run on a server that is similar to the production server. For WordPress development, this staging environment could be a secret test site on your production server. Finally, if the solution behaves as expected on the staging server, it can then be deployed to the live production server. The production server or servers are the ones that serve the website to the Internet. Using this three-tier workfl ow, developers are able to capitalize on the benefi ts of local development.

Why So Much Process? Now that you have a basic understanding of the workfl ow, you’ll circle back to why a developer should take these extra steps on the path to code deployment. While multiple phases seem at odds with a “get code working quickly” mantra, the benefi ts outweigh the overhead. First, as explained earlier, developing locally allows the developer to test and try things without breaking the live website. Truly, this can be one of the most important aspects of this system. Once your website has grown beyond the hobbyist audience, you want to minimize downtime. Developers should not be trying things on the live website. The second benefi t is privacy. Developing locally means your project is only available on your local workstation, or sometimes your local area network. You are in control of who is able to access it.

c03.indd 42 12/6/12 1:14 AM Tools for Component Administration ❘ 43

If you are developing on a public web server, however, while there are ways to restrict access, your potential audience is global. This privacy gives you the opportunity to try things and play around. Think of it as your own private WordPress sandbox with no one watching. For example, you might want to try the Ninja Warrior obstacle course or even the Wipeout obstacle course, but you do not want a global audience while you try to fi gure it out. There is no shame in attempting something and failing, but when working on a project, you probably don’t want it to be globally accessible while still in the development phase. While in development, your project could have security issues that have not been addressed yet and putting those on a production server puts the server at risk. Developing locally can save time and is often one of the biggest boosts to productivity. When working locally, you do not need a connection to the Internet to test your code. Your project is self-contained on your workstation. This also means you do not have to push your fi les to a remote server to test them. You simply need to save your edits and refresh your browser. The time waiting for FTP connections can add up. If you are developing a new theme, you can test your theme using different sets of content. For example, you may be building a custom theme for a specifi c project with an initial set of content, but you want to ensure that, in the future, new content added to the site is properly styled. Or you want to release your theme to the WordPress repository. While developing your theme on your local workstation, you can use different content than what is on the live site to make sure everything is formatted how you expect. This is part of the privacy of developing locally. Just because the initial website will have a certain content set for launch does not mean your local version must have the exact same content. This concept is covered in greater detail later in this chapter. Locally, you can run multiple instances of WordPress. Furthermore, each instance can be a different version of WordPress. This allows you to track changes to the core WordPress and make sure your code will continue to run on future revisions. For example, you can test your theme or plugin on one local site that is running the current version of WordPress, but you can also have a second WordPress site on your workstation that is running the beta version of the next release, or tracking the nightly development release. This helps you keep on top of changes to the WordPress core that might affect your project. There are many benefi ts and reasons to develop locally. In addition, for individual developers, there may be other reasons besides the privacy, security, and fl exibility benefi ts outlined here. Every developer will have to do his own cost benefi t analysis for each reason and determine if the risk or extra steps are worth the effort. At the end of this chapter, you will touch on some of the ongoing challenges with developing locally and moving your project through the development and deployment workfl ow. It is remarkably easy to set up a local WordPress development environment, using freely available tools that manage the major underlying components of the WordPress system: the web server with a PHP interpreter and the MySQL database.

TOOLS FOR COMPONENT ADMINISTRATION

Think about the prerequisites for WordPress, and then make a shopping list of the components you need for WordPress. WordPress is a . That means you need a web server. WordPress runs on PHP, a programming language for the web. That means your web server must support PHP.

c03.indd 43 12/6/12 1:14 AM 44 ❘ CHAPTER 3 WORKING WITH WORDPRESS LOCALLY

Apache is a good (and very popular) general-purpose web server that supports PHP, although there are many others that will work as well, including Microsoft IIS or Nginx. With WordPress version 3.2, the minimum version of PHP that is required is version 5.2.4. Ideally, you would like a web server that supports URL rewriting to make your permalinks work. Apache has a module called mod_rewrite to make this work. WordPress also needs a database to store the content of the site. WordPress only supports MySQL for the database and, as of version 3.2, the MySQL version must be 5.0 or greater. In addition, your PHP must have the appropriate MySQL libraries to make the database connection. You will also want a client to manage your database.

Getting Your Development Stack This sounds like a confusing and daunting list. But while many of us think of WordPress as the platform that you build your projects on, WordPress is, in turn, built on a platform. Commonly called the LAMP (Linux, Apache, MySQL, and PHP) stack, it has been the foundation for many Internet projects, including Facebook. And it is also the same foundation needed for WordPress. This means that the WordPress community is not the only one that has these requirements. As previously mentioned, this foundation is commonly called LAMP where the L stands for Linux. If you are running Linux as your workstation operating system, you can install the LAMP stack using your Linux distribution’s package management system. For example, if you are on a Debian or Debian derivative you could run apt-get install apache to install the Apache web server. A common trick is to install PHPMyAdmin as the MySQL client, that is, run apt- get install phpmyadmin . PHPMyAdmin is a web application that requires Apache, PHP, and MySQL, and because it is the MySQL client, it will install the appropriate libraries to connect PHP and MySQL. More than likely, you are not running Linux as your desktop operating system. You can install each component individually and connect all the moving parts for it to work. That would be the hard way. Luckily for us, there are some industrious people who have put together several packages that make installing and confi guring this LAMP foundation easy, and these packages exist for the various operating systems. If you are running Mac OS X, you can use the MAMP installer. We hope you can put together that this stands for Macintosh, Apache, MySQL, and PHP. You can download MAMP from http://www.mamp.info. Download MAMP, unpack it, and install it as you would any other Mac application. Once you drop it in your Applications folder, you can start MAMP and open your control panel. This is the control panel that controls the whole MAMP foundation, including your settings. One thing that we do not like about MAMP is that it does not use the default port for Apache. The standard for web servers to answer and respond is port 80, and browsers know this, which is why you never see an 80 in your browser’s address bar. MAMP, however, defaults to port 8888. That means that when you try to access your local web server, you will have to browse to http://localhost:8888 with your browser. Just keep that in mind as the examples in this book will be treated as though they’re running on the standard port 80.

c03.indd 44 12/6/12 1:14 AM Tools for Component Administration ❘ 45

If you are running a Windows workstation, you have a couple of options. Notably, there is WAMP and XAMPP. WAMP is Windows-specifi c and available from http://wampserver.com. WAMP, obviously, stands for Windows, Apache, MySQL, and PHP. XAMPP runs on Windows but is also cross-platform and is available from http://www.apachefriends.org. The X in XAMPP stands for cross-platform and the extra P is included because XAMPP includes , another programming language. They are both good options. Download and install WAMP as you would any other Windows application. Once it is installed, you will have a new Windows system tray icon for WAMPSERVER that functions as your control panel. Note that this foundation is actually several different applications working together in unison to provide you with a web development platform that happens to power WordPress. These WAMP and MAMP installers are purely automating the wiring of these packages together for a general-purpose use. Each individual application also has individual confi guration fi les that you can adjust to meet your needs. Some common confi guration changes are covered later in this chapter.

Adding WordPress to the Local Install Now that you have a working foundation, you need to install WordPress. You will want to stop and consider how you intend on using this local development environment. Do you need only one installation of WordPress? If you want more than one, are you going to use subfolders or set up individual websites using virtual hosts? Are you going to use WordPress Multisite functionality for multiple sites? The next section discusses some of these options, but for now, take the simple route and set up one WordPress site. To install WordPress, you can use the same source code control method using git or subversion, as shown in the Chapter 2. Or you can use the traditional method of downloading the installation fi les from http://wordpress.org. Either way, once you have the WordPress core fi les you will need to put them in your web server’s document root. For MAMP, this is set up under the MAMP control panel ➪ Preferences ➪ Apache. You can accept the default or set this document root to wherever you would like. Commonly, Mac users put the document root in the Sites folder of their Mac.

The WAMP document root defaults to c:\wamp\www. You can quickly access this folder using the www directory option from the WAMPSERVER start tray option. Copy your WordPress core fi les to the appropriate document root folder on your workstation.

Now open a web browser and browse to http://localhost. Do not forget that if your local web browser is not on the standard port, you may need to add that to the address bar. Also, if you copied WordPress into a subfolder of the document root, you may need to add that suffi x to the URL — for example, http://localhost/ddamstra/Documents/www. If your web and database servers are confi gured correctly, WordPress will create its databases and edit confi guration fi les, and you should see the fi rst page of the WordPress installation, as shown in Figure 3-1.

c03.indd 45 12/6/12 1:14 AM 46 ❘ CHAPTER 3 WORKING WITH WORDPRESS LOCALLY

FIGURE 3-1: WordPress installation

As with any WordPress installation, you will need to have your database and database access credentials set up. Both WAMP and MAMP come with PHPMyAdmin to manage the MySQL. Use the WAMP or MAMP control panel to access PHPMyAdmin and set these up. Lastly, do the infamous 5-minute WordPress install, as covered in Chapter 1. If you have problems with getting your local development environment working, seek assistance through the appropriate support communities and documentation. While they are designed to be simple installations of the various components, every workstation is different and managing the confi guration of these assorted moving parts is outside the scope of this book, and only tangentially WordPress-related.

CONFIGURATION DETAILS

In the previous section, you walked through how to set up a local development environment. While that section didn’t include an in-depth discussion, the basic idea is there. This section is about extending that environment and covers some tips to help you get the most out of working locally. Again, some of these pointers are about the LAMP foundation itself. Here you will dig into confi guration options in more detail. This section walks you through managing the fi lesystem tree seen by the web server, enabling debug data, and creating virtual server names.

Managing the Web Server Document Tree In the previous section, you accepted the default document root for Apache. However, for various reasons, that may not be the best spot for your workfl ow or backup systems. For example, in your development shop with multiple web developers, you may remap your Apache document root to c:\www. This way, everyone’s document roots are all identical and it’s a top-level folder that is easily accessible. Conversely, on your personal laptop, you may remap your document root to C:\Users\ddamstra\Documents\www because the Documents folder is backed up when connected to your home local area network.

c03.indd 46 12/6/12 1:14 AM Confi guration Details ❘ 47

Use caution when making changes to the confi guration. As mentioned many times, there are multiple moving parts involved and throwing one part out of alignment can have signifi cant consequences. MAMP allows you to change your document root through the control panel. With WAMP, you edit the confi guration fi le for Apache. This fi le is called httpd.conf and can be found in your WAMPSERVER control panel under the Apache fl yout. Change the line that reads document root to indicate your chosen location, as shown in Figure 3-2.

FIGURE 3-2: Apache document root

You will also need to change the Directory directive to match, as shown in Figure 3-3.

FIGURE 3-3: Apache Directory directive

c03.indd 47 12/6/12 1:14 AM 48 ❘ CHAPTER 3 WORKING WITH WORDPRESS LOCALLY

Using the WAMP control panel, you will need to restart Apache (or all services) for this change to take effect. If you previously had fi les in the old document root, you will need to move them to the new document root for them to be accessible. Take a moment to contemplate what you are publishing in your document root. You don’t want to publish any private or confi dential data. Consider which source code control system you are going to use. Is your source code control system also part of your deployment strategy? Make sure that if you are using a public repository such as GitHub that you do not push your wp-config.php fi le and expose your passwords. Likewise, if your development environment is accessible on your local area network, ensure you aren’t checking in confi guration fi les with sensitive information. Some source code control systems, notably subversion, store revisions in plain text in fi les in your project folder, potentially exposing credentials. This has happened to us on more than one internal penetration test exercise and the following is now part of your standard Apache confi guration. You can confi gure your Apache to not serve these .svn directories by adding the lines shown in Figure 3-4 to your httpd.conf fi le.

FIGURE 3-4: Apache block .svn fi les

Enabling Debug Information When developing locally, you want to address as many potential errors and warnings as possible. At the very least, you need to be aware of them. For development, you should set your PHP error condition as high as possible to show these errors to you so that you can attend to them. As discussed in Chapter 13, this is the exact opposite of what you want to do on your production server. On your production server, you want to hide all the errors from your visitors. On your local workstation, you are the only visitor, so you want to see them all since the errors are what you are working on.

c03.indd 48 12/6/12 1:14 AM Confi guration Details ❘ 49

You set your PHP error level in the php.ini fi le. With WAMP, you can access this fi le through the WAMP control panel, under the PHP fl yout. Set your error reporting directive to be E_ALL and E_STRICT, as shown in Figure 3-5.

FIGURE 3-5: PHP error level

Until PHP version 5.4, the strict warnings and notices have not been included in the E_ALL level. By setting the error reporting directive as mentioned, you will ensure that you are seeing the most error reporting possible, and coding to reduce these notices will ensure that you are providing the most PHP interoperability. Again, you will need to restart Apache to make this setting take effect. As previously mentioned, when developing on one operating system and deploying on another, you have to consider not all systems have the same PHP API. For example, the PHP $_SERVER[] has values on Windows machines that are not on Linux machines. Windows is not case sensitive in the fi lesystem, but Linux is. Developers have to remember that the target system may not be their development system. This is why you want the staging server to match the production server, so that discrepancies can be caught before being deployed. When developing locally, enable WordPress debugging. Similar to the PHP error reporting, this allows the developer to see and address WordPress issues. Likewise, this should always be disabled on production websites.

Enable WordPress debugging by editing your wp-config.php fi le and setting WP_DEBUG to true, as shown in Figure 3-6. Unlike the previous Apache and PHP settings, which were global to all sites on your workstation, this setting is per WordPress installation.

c03.indd 49 12/6/12 1:14 AM 50 ❘ CHAPTER 3 WORKING WITH WORDPRESS LOCALLY

FIGURE 3-6: WordPress debug

Handling Local and Production Database Out of the box, WordPress has confi guration for one database. When working locally, you want your development site to connect to your local MySQL so you do not risk messing up the production database. In other words, that is one of the reasons you are doing this.

A common method is to set the database host to be localhost and set your MySQL credentials and table name locally to the same as the production site. This is bad for security. Mark Jaquith offers an alternative solution that allows for both a production and a local workstation set of database access credentials on his site at http://markjaquith.wordpress.com/2011/06/24/ wordpress-local-dev-tips/. Essentially, he changes the wp-config.php fi le to look for an overriding set of credentials that exist on his development machine only. He then ignores this wp-config-local.php fi le in his source code control so that each developer can have his or her own controlled local credentials and so that this fi le never makes it to production.

Creating Virtual Local Server Names Initially, you set up WordPress in the document root of your local Apache. If you wanted more than one local website, you could set each website in its own folder. This works and you use it for many development sites. However, you can also set up each web server to respond to a local “fake” domain name. Sometimes, when moving to production, using this method makes the conversion from development to production easier. Here is how it works using some networking magic. Everyone is familiar with the common top-level domain names, such as .com, .net, and .org, and there are, in fact, many more with even more on

c03.indd 50 12/6/12 1:14 AM Confi guration Details ❘ 51

the horizon. These fully qualifi ed domain names work through the DNS system where web browsers ask these Internet-accessible DNS servers for the IP address of the website domain you typed in. However, your web browser uses the DNS resolver to check a local fi le fi rst to see if there is predefi ned mapping. This fi le is called the hosts fi le. You can use this fi le and matching Apache confi gurations to make your workstation access local sites with fake fully qualifi ed domain names. There are a couple of approaches to this. Some developers set the domain name of the actual site they are working on to be their local workstation instead, pre-empting DNS requests. That means that until they revert these changes, they cannot access the live site, and all requests will go to the local site. For example, instead of having requests for mirmillo.com go to the server’s publicly accessible IP address, these requests are intercepted and are redirected to the localhost IP address, which is always 127.0.0.1. The other option is to set the development site with a fake name that is easy to replace in SQL during the deployment phase. In this case, we set the local development site to be mirmillo.local, which is an invalid top-level domain name (for now). This way, we can access mirmillo.com through traditional DNS and still work on our local development version by accessing mirmillo .local in our web browser. This is the example you are going to follow in this book. First, you have to set up your Apache to support virtual hosts. The actual confi guration here is going to vary depending on your Apache installation. Using WAMP, the fi rst step was to set up a virtual host in Apache. This is done by editing the httpd-vhosts.conf fi le found in C:\wamp\bin\ apache\Apache2.2.11\conf\extra. The default example comes with two sample virtual hosts. Change one of the existing examples to become your localhost virtual host. Then change the second example to match the settings you need for your local installation, such as mirmillo.local, as shown in Figure 3-7.

FIGURE 3-7: mirmillo.local virtual host

c03.indd 51 12/6/12 1:14 AM 52 ❘ CHAPTER 3 WORKING WITH WORDPRESS LOCALLY

Next, you have to direct Apache to include this fi le. This is done by editing your httpd.conf fi le as you have done previously in this chapter. As shown in Figure 3-8, uncomment the line to include the virtual host confi gurations settings.

FIGURE 3-8: Apache includes virtual host confi g.

Next, edit your hosts fi le. On Mac OS X, this fi le is found in /private/etc/hosts and Linux has this fi le at /etc/hosts. On Windows, this fi le is C:\Windows\System32\drivers\etc. In short, this fi le is made up of IP address and domain name pairings. As shown in Figure 3-9, you can add a new mapping for mirmillo.local.

FIGURE 3-9: Hosts fi le mapping for virtual host

c03.indd 52 12/6/12 1:14 AM Deploying Local Changes ❘ 53

Finally, restart Apache and browse to http://mirmillo.local to complete the WordPress installation, as you did in Chapter 1.

Local Theme and Plugin Development If you are developing a theme, one of the benefi ts of developing locally is that you do not have to use the content that will be on the live site. In fact, if you are developing a theme that you plan to release to the population at large, you should use a content fi ller to make sure you style the vast spectrum of content. For example you can use the WordPress sample content available at http://codex .wordpress.org/Theme_Unit_Test. There are several alternative sample content import fi les such as the one provided by WPCandy at http://wpcandy.com/made/the-sample-post-collection, but the WordPress Theme reviewers will use theirs to approve your theme to be in the repository. You can review the entire Theme Repository checklist at http://codex.wordpress.org/Theme_ Development_Checklist, and this is covered in Chapter 9, “Theme Development.” Say you are developing a theme and you want to test it with the sample content mentioned in the previous paragraph, but you also need to target specifi c content for the actual site you are developing the theme for. Here is a good use for WordPress Multisite. WordPress Multisite is covered in depth in Chapter 10, including how to set it up. But once you have it set up locally, WordPress Multisite allows you to leverage the same themes and plugins across multiple WordPress sites in a WordPress network. We set this up so that one of our WordPress sites has the sample content. Then we created a second site for the site-specifi c content. When you give this a try, you should network-enable the theme you are developing and activate it on both sites. This allows you to jump back and forth in your browser to two different WordPress content sets but only edit one set of theme fi les. Likewise, if you are developing a new plugin, test it in WordPress Multisite to make sure it works. You can also set up several virtual hosts on your machine running different versions of WordPress, both a few revisions back and also development releases to make sure your plugin will continue to work with the next update. Although we all preach to users to keep WordPress current, the reality is that some sites lag behind, either because of hosting restrictions, ignorance, or laziness. It’s important to make sure your plugin continues to work if you want people to use it. Also see Chapter 8, “Plugin Development.” Now that you have your new project working locally, and you have removed all the errors and notices from WordPress and PHP, you are ready to push it to a live server. In the next section, you will look at some of the challenges and tactics for pushing code live.

DEPLOYING LOCAL CHANGES

First, distinguish between the different types of objects you are deploying. There is code, which could be plugin code, or theme and theme assets. There is content, which is the website subject matter from the posts and pages and is stored in the database. Finally, there is the confi guration, which is also stored in the database. Deploying the code is easy. Developers do this every day. One of the advantages of PHP and WordPress is that you can generally drop code into the document root and it runs at the next

c03.indd 53 12/6/12 1:14 AM 54 ❘ CHAPTER 3 WORKING WITH WORDPRESS LOCALLY

request. Deploying code is simple and you can use your FTP client to do it. But please use SFTP, if possible, because it is a secure protocol, whereas FTP is not. Deploying the content and the confi guration is more diffi cult. WordPress uses fully qualifi ed links in all the content. So every internal HREF and menu item has the full domain name embedded. Likewise, the confi guration of the site is also tied to the domain name that WordPress was installed at. You can’t simply take a database dump and move it. There is, however, an intermediary step to change the domain names in the database export before importing it into the production site. Use caution here that you are not going to steamroll any updated content on the live site with your content from the development site. How exactly you do this in your situation is dependent on your exact needs, but overall, this process is very similar to a situation in which you are moving your site from one domain to another. The process is extensively documented in various websites, the WordPress codex at http://codex.wordpress.org/Moving_ WordPress, and many other tutorials. This is just one method. In short, this is how the process works for us, assuming you want to move all content from your development database to the live site. You are going to remove all the fully qualifi ed links from the content on your development site. All future content you add to the production site, once the content is moved, will be fully qualifi ed, but this is a method to make all the URLs root relative and then they will work on both your development site and the live site. For this process you use the wp-DBManager plugin by Lester Chan. This plugin allows you to make database backups and also perform SQL queries on the data. You could also use WordPress’s built-in database export functionality and PHPMyAdmin to do the same.

Pretend you are moving from the local development site mirmillo.local to the live production site of mirmillo.com. This is where using the “fake” domain name virtual host option mentioned previously comes in handy. Using the plugin, make a backup of your working test site. Download and save this backup fi le in case things go awry. Next, in the SQL page of the plugin, you will run the queries shown in Figure 3-10 to update the URLs in your site’s content. Essentially, you are removing the domain name from the URLs in the HTML code. Now export your content from your development site. Content export is found in your WordPress dashboard under Tools ➪ Export. Download this fi le. This is your movable content with root relative links. FIGURE 3-10: SQL queries to remove domain names

c03.indd 54 12/6/12 1:14 AM Summary ❘ 55

Import this content into your live site. The import functionality is found in your WordPress Dashboard under Tools ➪ Import. Again, be cautious that you do not overwrite newer content or content you want to keep. Truly, it is not a diffi cult process; it is just one that requires some planning and coordination. There are some developers who are working on tools to make this process easier. In particular, we have been keeping an eye on RAMP by Alex King’s Crowd Favorite, available online at http://crowdfavorite.com/wordpress/ramp/. While you haven’t tried it yet, it looks promising. The challenge is always that, when using WordPress as a Content Management System, users can and will log in to the production site and make changes — that’s the point. But in doing so, your development content gets out of sync. Ultimately, the goal will be to have a way to synchronize WordPress databases between live, staging, and development and be able to handle confl ict resolution. There is no silver bullet here, but it seems to be a challenge that many developers are working on.

SUMMARY

This chapter reviewed some of the reasons and processes for a proper development workfl ow. In addition, it covered how to enable a local WordPress development environment in your own private sandbox. Finally, you examined a process to push a development site to a production server. The next chapter digs into the core fi les of WordPress and reviews how WordPress works.

c03.indd 55 12/6/12 1:14 AM c03.indd 56 12/6/12 1:14 AM 4 Tour of the Core

WHAT’S IN THIS CHAPTER?

➤ Exploring the WordPress core fi les ➤ Searching through core fi les as reference ➤ Working with the WordPress Codex ➤ Understanding inline documentation

To understand how to extend WordPress properly, you must fi rst learn how the core of WordPress functions. This will help you learn what tools are available in the WordPress core to make your life easier. WordPress handles most of the tedious coding and logic problems for you. The WordPress core is the best resource for learning how WordPress works. The beauty of open source software is you have all of the code at your disposal. If you are ever unsure how a certain aspect of WordPress functions, just start digging into the code! The answers are all there; it’s just a matter of fi nding and understanding them.

WHAT’S IN THE CORE?

The WordPress core is powered by a set of fi les that are part of the original WordPress software download. These are required “core” fi les that WordPress needs to function properly. The core fi les are expected to change only when you upgrade WordPress to a newer version. The core does not include your custom fi les for plugins, themes, database settings, the .htaccess fi le, and so on. The core also does not include any media you have uploaded to WordPress. Basically, any fi les added to WordPress after installation are considered outside of the core.

c04.indd 57 12/6/12 1:15 AM 58 ❘ CHAPTER 4 TOUR OF THE CORE

The WordPress core fi les are primarily PHP fi les, but also contain CSS, JavaScript, XML, HTML, and image fi les. These fi les control everything about WordPress including how content pages are generated to display, loading the confi gured theme and plugins, loading all options and settings, and much more. In short, the core contains several major function types: ➤ Posts, pages, and custom content — Creating, storing, retrieving, and interacting with the majority of your WordPress content. The discussion of the loop that controls content display and ordering in Chapter 5 relies heavily on these functions. ➤ Metadata — Everything from tags and categories to user-created taxonomies. The data models used are explored in Chapter 7. ➤ Themes — Supporting functions for WordPress themes. Theme development and its relationship to these functions are discussed in Chapter 9. ➤ Actions, fi lters, and plugins — Framework for extending WordPress, covered in more detail in Chapter 8. ➤ Users and authors — Creating and managing access control to your site, and key to the security and enterprise use topics in Chapters 12 and 14. ➤ Feeds, formatting, and comments — These are discussed as needed throughout the book.

This chapter digs into these fi les as you explore the WordPress core fi les. Think of this chapter as your guidebook to the “how” of exploring the WordPress core; it is a fi eld guide companion to the WordPress Codex documentation for user-contributed discussion and explanation. It’s also impera- tive to be comfortable browsing and searching the core to complement the functional introduction provided here. An exhaustive list of every WordPress function is not included here, both because the list changes and evolves as the WordPress core undergoes continuous development, and because the goal here is to convey developer and deployer expertise and not to summarize the Codex. WordPress comes packaged with two plugins: Akismet and Hello Dolly. These two plugins exist in your plugins directory inside wp-content. Even though these two plugins are a part of the WordPress core fi le package download, they are not considered core functionality because they must be activated to function and can easily be removed. WordPress also comes packaged with three core themes: Twenty Ten, Twenty Eleven, and Twenty Twelve. Twenty Twelve is the default theme on a fresh installation of WordPress. As with the included plugins, these theme fi les are not considered core functionality because they can easily be replaced with any theme that you want to use on your website.

USING THE CORE AS A REFERENCE

To use the WordPress core as a reference, you need to understand what to expect in the core fi les. Most WordPress core fi les contain documentation in the form of code comments. Typically, a code comment is displayed in the header of the fi le and gives an overall summary of the core fi le you are viewing.

To see this fi rst-hand, open the wp-login.php fi le located in the root directory of WordPress. You’ll notice the top of the fi le has a header comment describing the fi le’s function:

c04.indd 58 12/6/12 1:15 AM Using the Core as a Reference ❘ 59

/** * WordPress User Page * * Handles authentication, registering, resetting passwords, forgot password, * and other user handling. * * @package WordPress */

All core fi les, other than images, can be viewed using a text editor program. Depending on your default program settings, you may need to open up your text editor fi rst and then open the fi le rather than just opening up the fi le directly. It’s also helpful to use a text editor that has syntax highlighting, meaning PHP syntax would be highlighted to help you read the code easier.

There is a full list of compatible text editors on the WordPress.org Codex at http://codex .wordpress.org/Glossary#Text_editor.

Inline Documentation Nearly all WordPress core fi les contain inline documentation in PHPDoc form. PHPDoc is a stan- dardized method of describing a function’s usage in PHP comment form. This means each function is explained in detail directly before the function in a comment block. The following is the defi ned template for documenting a WordPress function:

/** * Short Description * * Long Description * * @package WordPress * @since version * * @param type $varname Description * @return type Description */

This is amazingly helpful in understanding how functions work. The comment includes a short and long description. It also includes the version of WordPress it was added in. This helps distinguish new functions added to WordPress when a new version is released. Available parameters are also listed along with the parameter data type. A data type is the type of data that is required for the parameter. For example, an ID parameter would likely use the int (integer) data type. The fi nal piece of information is the return value. The return value data type is also listed. All new functions added to WordPress are documented using the preceding template. For more information on inline documentation in WordPress, see this Codex article: http://codex.word press.org/Inline_Documentation.

c04.indd 59 12/6/12 1:15 AM 60 ❘ CHAPTER 4 TOUR OF THE CORE

Finding Functions Looking up a function in the core is the quickest way to learn how a specifi c WordPress function works. You can see exactly what parameters are allowed to be sent to the function, as well as what the function actually does and what the return values are. To start, make sure you have downloaded the latest version of WordPress locally to your computer. You will search these fi les as a reference for WordPress. Open up any text editor you have that can search fi les (TextPad for Windows and Textmate for Mac are recommended). When searching for a function, you want to eliminate calls to that function from your search. Do this by includ- ing the word “function” at the start of your search, as in function wp_head. Not everything in WordPress is a function, but this is a good place to start. If you don’t fi nd any matches, remove “function” from the beginning of your search. Also remember to set your text editor to search all fi les (*.*), not just .txt fi les.

Let’s look at the is_super_admin() function. This function is used to check if a user is a super admin in WordPress Multisite. You need to know exactly what values the function expects before you can use it. Open your text editor and search all fi les in WordPress for function is_super_admin. The search should produce one result in wp-includes/capabilities.php:

function is_super_admin( $user_id = false ) {

Right away, you notice one parameter that can be sent to this function: $user_id. Notice the inline documentation listed directly above the function. In this case, the is_super_admin() documenta- tion looks like this:

/** * Determine if user is a site admin. * * @since 3.0.0 * * @param int $user_id (Optional) The ID of a user. Defaults to the current user. * @return bool True if the user is a site admin. */

This is an extremely valuable block of content. The comment has a short description about what the function does, in this case “Determine if user is a site admin.” The comment also notes when the function was added (since version 3.0.0). There is also information about the single parameter, including the parameter type, what the parameter is responsible for, and the fact that the parameter is optional in this case. The comment also details what the expected return values will be. In this case, the function will return True if the user is a site admin and False if not. This alone is enough information to understand how this function works, but let’s dig into the code for a better understanding. The fi rst few lines look like this:

if ( $user_id ) $user = new WP_User( $user_id ); else $user = wp_get_current_user();

c04.indd 60 12/6/12 1:15 AM Using the Core as a Reference ❘ 61

Based on the PHPDoc comment above the function, you know the $user_id parameter is optional, so this code shows what happens if a $user_id parameter is not passed to the function. The preced- ing if statement checks if the $user_id variable contains a value. If it does, the WordPress User class, WP_User, is called to retrieve the user data for that user ID. If the $user_id variable is empty, the wp_get_current_user() function is called to get the user data for the currently logged in user.

Next, the function checks that the $user data actually exists before proceeding and, if not, will return false.

if ( ! $user->exists() ) return false;

Now that you know the $user data exists, you need to check if that user is actually a super admin:

if ( is_multisite() ) { $super_admins = get_super_admins(); if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins ) ) return true; } else { if ( $user->has_cap('delete_users') ) return true; }

Let’s break down this if statement a bit:

if ( is_multisite() ) {

This if statement checks that Multisite is actually running in WordPress by calling the is_mul- tisite() function. Super admins will only exist if the Multisite feature of WordPress has been enabled.

Now that WordPress has determined Multisite is running, the function calls get_super_admins() to retrieve an array of all super admins in WordPress using the following code:

$super_admins = get_super_admins();

The $super_admins variable is now an array of all super admin login usernames. The next line is the most important line in this function. This is the code that actually checks that a user is a super admin in WordPress:

$super_admins = get_super_admins(); if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins ) ) return true;

Before working with an array, you always want to verify the variable is an actual array using the is_array() PHP function. The second part of this line of code uses the in_array() PHP function to check if the user’s login exists in the super admin array. If it exists, the user is a super admin and the function returns true.

c04.indd 61 12/6/12 1:15 AM 62 ❘ CHAPTER 4 TOUR OF THE CORE

If the is_multisite() check covered earlier returns false, the function will execute the following else code:

} else { if ( $user->has_cap('delete_users') ) return true; }

The preceding code checks if the user has the delete_users capability. By default, this capability is assigned to regular administrator accounts in WordPress. If Multisite is disabled in WordPress, but you are an administrator, this code will return true when calling the is_super_admin() function. The fi nal line of code in the function is:

return false;

This code basically says that if any of the checks in the is_super_admin() function fail, return false. This is more of a safety measure to be certain a true or false value is always returned. After viewing this example, it should be more apparent how useful the WordPress core code can be. You learned exactly how this function works by exploring the source code. All the answers to your questions exist within the core so it’s essential to have a good understanding of how to utilize the core to your advantage.

Exploring the Core The WordPress core has certain fi les that contain many of the most popular WordPress functions. These functions are used for all WordPress APIs and can be used in any custom plugin or theme. The following sections detail the WordPress core fi les that contain key pieces of code for working with WordPress. All of the fi les listed in the section that follows are located in the /wp-includes directory of WordPress.

Functions.php The functions.php fi le contains the main WordPress API functions. These functions are used to easily interact with WordPress using a standardized method. Plugins, themes, and the WordPress core all use these functions:

➤ current_time — Retrieves the current time based on specifi ed type. ➤ force_ssl_login — Requires SSL (https) login to WordPress. ➤ wp_nonce_field — Displays a nonce hidden fi eld for forms. A nonce fi eld is used for verifi cation purposes when submitting and processing data in WordPress. This is a critical step in securing your code. ➤ absint — Converts value to nonnegative integer.

c04.indd 62 12/6/12 1:15 AM Using the Core as a Reference ❘ 63

Option.php The option.php fi le contains the main WordPress Options API functions. These functions are used for the following:

➤ add_option, update_option, get_option — Functions to create, update, and display a saved option. ➤ set_transient, get_transient, delete_transient — Functions to create, retrieve, and delete transients in WordPress. A transient is an option with an expiration time. When the expiration time is hit, the transient is automatically deleted in WordPress. ➤ add_site_option, update_site_option, get_site_option — Functions to create, update, and display site options. If Multisite is enabled, function returns the network option; if not, the standard site option is returned.

Formatting.php The formatting.php fi le contains the WordPress API formatting functions. These functions format the output in many different ways:

➤ esc_attr — Used to escape a string for HTML attributes. ➤ esc_html — Used to escape a string for HTML. ➤ esc_url — Used to check and clean a URL. ➤ sanitize_text_field — Sanitizes a string from user input or from the database. ➤ is_email — Verifi es that an e-mail is valid. ➤ capital_P_dangit — Famous fi lter that forces the P in WordPress to be capitalized when displaying in content.

Pluggable.php The pluggable functions fi le lets you override certain core functions of WordPress. WordPress loads these functions if they are still undefi ned after all plugins have been loaded. Some of the more commonly used functions include:

➤ wp_mail — Sends e-mail from WordPress. ➤ get_userdata — Returns all user data from the specifi ed user ID. ➤ get_currentuserinfo — Returns user data for the currently logged-in user. ➤ wp_set_password — Updates a user’s password with a new encrypted one. ➤ wp_rand — Generates a random number. ➤ wp_logout — Logs out a user, destroying the user session. ➤ wp_redirect — Redirects to another page. ➤ get_avatar — Returns the user’s avatar.

c04.indd 63 12/6/12 1:15 AM 64 ❘ CHAPTER 4 TOUR OF THE CORE

Plugin.php The plugin.php fi le contains the WordPress Plugin API functions, including:

➤ add_filter — Hooks that the WordPress core launches to fi lter content before displaying on the screen or saving in the database. ➤ add_action — Hooks that the WordPress core launches at specifi c points of execution. ➤ register_activation_hook — Hook called when a plugin is activated. ➤ register_deactivation_hook — Hook called when a plugin is deactivated. ➤ plugin_dir_url — Returns the fi lesystem directory path for the plugin. ➤ plugin_dir_path — Returns the URL for the plugin.

User.php The user.php fi le contains the WordPress User API functions, including:

➤ get_users — Returns a list of users matching criteria provided. ➤ add_user_meta, get_user_meta, delete_user_meta — Used to create, retrieve, and delete user metadata. ➤ username_exists — Checks if a username exists. ➤ email_exists — Checks if an e-mail address exists. ➤ wp_insert_user and wp_update_user — Create and update a user account.

Post.php The post.php fi le contains the functions used in the post process of WordPress, including:

➤ wp_insert_post — Creates a new post. ➤ get_posts — Retrieves a list of the latest posts’ matching criteria. ➤ add_post_meta — Creates metadata (custom fi eld data) on a post. ➤ get_post_meta — Retrieves metadata (custom fi eld data) on a post. ➤ get_post_custom — Returns a multidimensional array with all metadata (custom fi eld) entries for a post. ➤ set_post_thumbnail — Sets a featured image on a post. ➤ register_post_type — Registers a custom post type in WordPress.

The plugin registration functions add_filter() and add_hook() are key to extending how WordPress processes content, and these functions let you extend the basic content data struc- tures used by WordPress. We cover custom post types and their data management in detail in Chapter 7.

c04.indd 64 12/6/12 1:15 AM Using the Core as a Reference ❘ 65

Taxonomy.php The taxonomy.php fi le contains the functions used by the WordPress Taxonomy API. Taxonomies are used to manage the hierarchical relationships of metadata such as categories and tags (described in Chapter 6) and can also be extended, as you’ll explore in Chapter 7. Functions in this fi le include:

➤ register_taxonomy — Registers a custom taxonomy in WordPress. ➤ get_taxonomies — Returns a list of registered taxonomies. ➤ wp_insert_term, wp_update_term — Insert or update a taxonomy term based on argu- ments provided.

There are many more core functions that can be used when developing custom themes and plugins for WordPress. Take a few minutes and explore the core fi les inside /wp-includes. This directory contains most of the WordPress API core function fi les. To learn more about any function listed here, open up the corresponding fi le and view the source code. Remember that each function will have inline documentation explaining how to utilize the function correctly. We cover the Plugin API functions in more detail in Chapter 8. The core func- tions used by themes are covered in Chapter 9.

Deprecated Functions When a new version of WordPress is being developed, certain functions may become deprecated. A deprecated function means the function is not removed from WordPress, but it should not be used in your plugins and themes going forward. Typically in such a case, a new function has been created to replace the deprecated function. A function may be deprecated in WordPress for many different rea- sons, but the most common is that the function needs a complete rewrite to better handle the feature it adds to WordPress. WordPress contains a fi le to store all functions that have been deprecated over the years. WordPress is known for having superior backwards compatibility. This means that when a new version of WordPress is released, a strong focus it put on backwards compatibility to verify new features and functions will not break existing sites running WordPress, even if the features in use are considered deprecated.

Let’s look at the inline documentation for the get_current_theme() deprecated function:

/** * Retrieve current theme name. * * @since 1.5.0 * @deprecated 3.4.0 * @deprecated Use (string) wp_get_theme() * @see wp_get_theme() * * @return string */

c04.indd 65 12/6/12 1:15 AM 66 ❘ CHAPTER 4 TOUR OF THE CORE

You’ll notice a few additional comment lines for deprecated functions. The fi rst is the @deprecated line stating in what version of WordPress the function was deprecated, in this case v3.4. The second is @see which tells you what function should be used instead, in this case wp_get_theme().

The deprecated.php fi le is a very important fi le to check when a new version of WordPress is released. If a common function is deprecated, you should immediately stop using it and even consider updating your old code to use the replacement. Generally speaking deprecated functions are usually not removed from WordPress core, but there is no guarantee a deprecated function won’t be removed in a future release.

WORDPRESS CODEX AND RESOURCES

WordPress has many different online resources that are extremely useful when learning and working with WordPress. These resources should be bookmarked for quick reference and are used by begin- ners and experts alike.

What Is the Codex? The WordPress Codex is an online wiki for WordPress documentation located on WordPress.org. WordPress.org describes the Codex as an “encyclopedia of WordPress knowledge.” You can visit the WordPress Codex by going to http://codex.wordpress.org or by clicking the Docs tab in the header of WordPress.org. The Codex is a wiki-based website, which means anyone can create, edit, and contribute to the articles within the Codex. The Codex is jam-packed with useful knowledge covering all aspects of WordPress. From, “Getting Started with WordPress,” to more advanced developer topics, the Codex is an essential resource for anyone looking to learn more about WordPress. The Codex is available in many different languages. To fi nd a Codex version translated in your language, visit the Multilingual Codex page at http://codex.wordpress.org/Multilingual_ Codex. You can also contribute to the Codex and help expand on any language or create your own language if it is not listed.

Using the Codex The Codex can be used in many different ways. The most common method is to search the Codex using the search box in the header, or you can visit http://wordpress.org/search/ to easily search through the Codex for appropriate articles matching your search criteria. The WordPress.org search is powered by Google Custom Search, as shown in Figure 4-1. The search results returned are from all of WordPress.org, not just the Codex, so it’s important to keep that in mind. There is a lesser known Codex-only search located at http://codex.wordpress.org/ Special:Search.

c04.indd 66 12/6/12 1:15 AM WordPress Codex and Resources ❘ 67

FIGURE 4-1: WordPress.org search

You can also navigate through the index of articles on the Codex homepage. These articles are organized by topic and generally ordered by level of diffi culty. There is also a topic toward the top for the latest version of WordPress. The articles here cover new features, compatibility tests for plugins and themes, installing, upgrading, and support for the new version. An extensive glossary of terms is available for the Codex. This can help familiarize you with common words used throughout the Codex. You can view the offi cial Codex Glossary at http://codex.wordpress.org/Glossary. Another search method is to use the quick index. This index allows you to look up an article by the fi rst letter of the article’s title. You can fi nd the quick index at http://codex.wordpress.org/ Codex:Quick_index.

A WordPress Lessons page is also featured in the Codex at http://codex.wordpress.org/ WordPress_Lessons. This page provides lessons on how to learn specifi c elements of WordPress. The lessons are organized by topic and are a great place to start if you are unsure what to read fi rst.

Function Reference WordPress functions are described in the Codex with an individual Function Reference page for each WordPress API function available. These pages explain in detail exactly how a WordPress

c04.indd 67 12/6/12 1:15 AM 68 ❘ CHAPTER 4 TOUR OF THE CORE

function works, as shown in Figure 4-2. Bookmark this page for a quick reference on WordPress functions and their capabilities. The offi cial Function Reference is located at http://codex.word press.org/Function_Reference.

FIGURE 4-2: Function reference for get_userdata

Think of the Function Reference as an online and expanded version of a function’s inline documen- tation. The reference has a description explaining how the function works and how it is used. The individual parameters are listed along with data types and a description of each. The most useful section of the Function Reference is the examples toward the bottom. The examples make it very easy to see exactly how to use the function. The get_userdata example is shown here:

user_login . "\n"; echo 'User level: ' . $user_info->user_level . "\n"; echo 'User ID: ' . $user_info->ID . "\n"; ?>

This example shows how to load specifi c user data for user ID 1. The example output is as follows:

Username: michael_myers User Level: 10 User ID: 1

This is a simple example, but this, along with the additional reference information, can help you eas- ily learn a new function and how to use it properly in your code.

c04.indd 68 12/6/12 1:15 AM WordPress Codex and Resources ❘ 69

The fi nal Function Reference topic lists related functions. This can help you identify a similar function that may accomplish that task you are working on. For example, the wp_insert_post() function lists wp_update_post() and wp_delete_post() as related functions. The majority of the WordPress API functions are well documented, but not all functions have a Function Reference page in the Codex. Any function displayed in red on the Function Reference homepage currently has no documentation. This is an ongoing community project so expect all functions to be fully documented in the Codex eventually.

NOTE Contributing to the Codex is a great way to get involved in WordPress. You don’t need to be an advanced developer to contribute code examples, descriptions, and additional information about various features and functions in WordPress.

WordPress APIs WordPress features many different APIs that help interact with WordPress. Think of the APIs as gateways that let you add code or retrieve external content within WordPress without violating the “don’t the hack core” maxim: most APIs insert references to non-core code that will be added to the wp-content directory by registering its entry points with WordPress. Each API is documented in the Codex along with functions used in the API. An API is a set of predefi ned functions available for use in themes and plugins. The following is a list of the most common WordPress APIs: ➤ Plugin API — Used for custom plugin development. The Codex features an extensive Plugin API documentation page. There is an introduction to Hooks, Actions, and Filters, which are the primary ways to interact with WordPress from a custom-built plugin. The Plugin API page links to the Function Reference pages for available API functions, which are located in /wp-includes/plugins.php.

http://codex.wordpress.org/Plugin_API

➤ Widgets API — Used to create and maintain widgets in your plugin. The widget will automatically appear under the Appearance ➪ Widgets SubPanel and can be used on any defi ned sidebar on your theme.

http://codex.wordpress.org/Widgets_API

➤ Shortcode API — Used for adding shortcodes in your plugin. A shortcode is a macro code added to a post. This allows a plugin to grab that shortcode and execute specifi c commands and display elements in place of it in your post. Shortcodes can also accept parameters to alter the output. An example core WordPress shortcode is [gallery]. A d d i n g [gallery] to your post auto- matically displays all images uploaded to that post in a gallery style. When editing a post, you will see the [gallery] shortcode, but viewing it on the public side of your website displays the actual gallery of images.

c04.indd 69 12/6/12 1:15 AM 70 ❘ CHAPTER 4 TOUR OF THE CORE

http://codex.wordpress.org/Shortcode_API

➤ HTTP API — Used for sending an HTTP request from WordPress. This API is a standard- ized method to grab the content of an external URL. Basically, it takes the provided URL and tests a series of PHP methods for sending the request. Depending on the hosting envi- ronment, WordPress uses the fi rst method it deems to be confi gured correctly to make the HTTP request. The current HTTP API PHP methods tested are cURL, Streams, and FSockopen. The methods are also checked exactly in that order. You can use the Core Control plugin (http://wordpress.org/extend/plugins/Core-control/) to specifi cally choose which method is used for all HTTP requests. Using the HTTP API, you could easily interact with the Google Maps API to dynamically generate maps and plots. The HTTP API can also easily interact with the Twitter API, allowing you to post/read tweets directly from WordPress.

http://codex.wordpress.org/HTTP_API

➤ Settings API — Used for creating a settings page. This API is used for creating and manag- ing custom options for your plugins and themes. The main advantage of using the Settings API is security. The API sanitizes all of the setting data saved by the user. This means no more worrying about nonces, data validation, and cross-site scripting (XSS) attacks when saving setting data. This is much easier than the old method of data validation, which you had to use each time you needed to save settings in a plugin.

http://codex.wordpress.org/Settings_API

➤ Options API — Used for storing option data in the WordPress database. The Options API provides an easy way to create, update, retrieve, and delete option values.

http://codex.wordpress.org/Options_API

➤ Dashboard Widgets API — Used for creating admin dashboard widgets. Widgets added from the API automatically contain all jQuery features that the core admin dashboard wid- gets have, including drag/drop, minimize, and hiding via screen options.

http://codex.wordpress.org/Dashboard_Widgets_API

➤ Rewrite API — Used for creating custom rewrite rules. This API allows you to create custom rewrite rules just as you would in your .htaccess fi le. You can also create custom permalink structure tags (that is, %postname%), add static endpoints (that is, /my-page/), and even add additional feed links. The Rewrite API functions are located in /wp-includes/rewrite.php at http://codex.wordpress.org/Rewrite_API.

Remember that all WordPress APIs can be used in custom plugin and theme development. This is the primary method of extending WordPress with additional features and functionality. Utilizing the preceding APIs creates an easy and standardized way of interacting with WordPress.

c04.indd 70 12/6/12 1:15 AM Don’t Hack the Core! ❘ 71

For more information on all WordPress APIs visit the Codex page at http://codex.wordpress .org/WordPress_API's.

Codex Controversy As with any wiki, there will always be controversy over the accuracy of the articles in the Codex. One problem that has plagued the Codex is the freshness of the articles. WordPress is being devel- oped at a decent pace and thus the Codex needs to keep up that pace in order to be accurate. Unfortunately, that doesn’t always happen, and some material is outdated. The WordPress Codex is a community project, so you can easily create an account and start helping out! Contributing to WordPress is covered in Chapter 16. Another problem that exists within the Codex is the organization of the content. Currently, there is so much information in the Codex that it can be hard and confusing to fi nd the answers you are looking for. Again, one of the motivations for this introduction to the WordPress core is to provide you with a map to help narrow the scope of your searches and to introduce related functional topics.

DON’T HACK THE CORE!

Whereas exploring the WordPress core and using it as a reference is highly encouraged, hacking the core is not. Hacking the core means making any changes to the core fi les of WordPress. A change could be as simple as one line of code, but a hack is a hack and doing so could cause major problems down the road.

Why Not? Hacking the WordPress core can make it very diffi cult to update to the latest version of WordPress. Keeping WordPress current is an important step in overall website security. If any security vulnerability is discovered, a patch is typically released very quickly. If you can’t update because you have modifi ed core fi les, you are opening up your website to these security vulnerabilities, and you increase the likelihood that your website will be hacked. Hacking the core can also lead to an unstable website because many parts of WordPress rely on other parts to function as expected. If you make changes to those parts, it could break something completely unrelated to what you have changed. Security is another reason why you shouldn’t hack the core. WordPress core is viewed and scru- tinized by security experts all over the world. By hacking the core, you are relying on your own expertise to make your hacks secure. If you don’t understand the many different ways a hacker can exploit your code, you might end up creating a security vulnerability within the core of WordPress. The fi nal reason why you should never hack the core is compassion: that is, compassion toward the developer who comes after you to maintain the website. Most websites will change developers over the years so there is no guarantee you will be working on a particular website fi ve years from now. Imagine the developer that follows you trying to determine what core fi les were hacked to make the website function. This can be a nightmare for any developer and it puts the website owner in a bad position because most developers will refuse to work on a hacked version of WordPress. If you hack

c04.indd 71 12/6/12 1:15 AM 72 ❘ CHAPTER 4 TOUR OF THE CORE

the core, you are building dependencies that will either be misunderstood or hidden, and when the WordPress core is upgraded for this site, the hacked core will break in silent, evil, or loud ways.

Alternatives to Hacking the Core Any feature or functionality that does not exist in WordPress can be added with a plugin. Sometimes a core hack may be the easy answer, but in the long run, it will make your life harder. (We have yet to come across a feature we needed that we couldn’t incorporate with a plugin.) WordPress is extremely fl exible, which is one of its major strengths, and therefore the core should never be hacked. Don’t hack the core! If you are fascinated by the WordPress core and its intricacies, you should join the WordPress Developer Community and get involved fi xing bugs and contributing to the core build of WordPress. This is covered in detail in Chapter 16.

SUMMARY

In this chapter you covered a tour of the WordPress core software. You explored what’s in the core, how to use the core as a reference when developing for WordPress, and how to determine what functions are deprecated each release. You also learned about the WordPress Codex and available APIs in WordPress. Now that you understand the core of WordPress, it’s time to learn how to utilize the WordPress Loop to customize the display of content.

c04.indd 72 12/6/12 1:15 AM 5 The Loop

WHAT’S IN THIS CHAPTER?

➤ Understanding the fl ow of the Loop and where it can be used ➤ Determining content display using the Loop ➤ Customizing the Loop with diff erent granularities of data access ➤ Using template tags ➤ Understanding global variables and their relationship to Loop processing ➤ Working outside of the Loop The Loop refers to how WordPress determines what content (posts, pages, or custom content) to display on a page you are visiting. The Loop can display a single piece of content or a group of posts and pages that are selected and then displayed by looping through the content; thus, it’s called the Loop. This is how WordPress displays blog posts by default. The Loop selects posts from the MySQL database based on a set of parameters, and those parameters are typically determined by the URL used to access your WordPress website. For example, the homepage might show all blog posts in reverse chronological order by default. A category page, accessed via a URL such as http://example.com/category/zombies, shows only blog posts assigned to that category, in this case pages put into the zombies category. An archive page shows only blog posts that are dated with that particular month and year. WordPress maps nearly every parameter about your posts into a selection variable, providing the basis for an equally wide number of different ways to alter the Loop’s content selection algorithm. It is very easy to customize what content is displayed, and where, on your website with a thorough understanding of how the Loop translates a URL into what you see when you access that link.

c05.indd 73 12/6/12 1:28 AM 74 ❘ CHAPTER 5 THE LOOP

This chapter discusses how the Loop works, where the Loop can be used, and the logical fl ow of the Loop. It also covers how to customize the Loop using the many different functions and data access methods available in WordPress. Global variables that maintain the current state are also discussed along with working outside of the Loop.

UNDERSTANDING THE LOOP

Understanding how the Loop functions will help you understand how you can control it. Controlling the Loop to display exactly the content you want will be one of your most used tools in developing WordPress-powered websites. Because the Loop is at the heart of every WordPress theme, being able to customize the display content opens up the doors to making WordPress look and act however you want. To understand the Loop, it helps to break down the steps WordPress takes to generate a page’s content: 1. The URL is matched against existing fi les and directories in the WordPress installation. If the fi le is there, it is loaded by the web server. WordPress doesn’t actually get involved in this decision; it’s up to your web server and the .htaccess fi le created by WordPress to decide if the URL is something handled by the web server or to be turned into a WordPress content query. This was covered in the discussion of permalinks in Chapter 2. 2. If the URL doesn’t load a WordPress core fi le, it has to be parsed to determine what content to load. The web server starts by loading the WordPress core through index.php to begin the setup for the Loop. For example, when visiting a specifi c tag page such as http://example.com/tag/bacon, WordPress will determine that you are viewing a tag and load the appropriate template, select the posts saved with that tag, and generate the output for the tag page. 3. The translation of URL-to-content-selection magic happens inside of the parse_ query() method within the WP_Query object that WordPress created early on in its process- ing. WordPress parses the URL fi rst into a set of query parameters that are described in the next section. All query strings from the URL are passed into WordPress to determine what content to display, even if they look like nicely formatted pathnames. If your site is using pretty permalinks, the values between slashes in those permalinks are merely parameters for query strings. For example, http://example.com/tag/bacon is the same as http://example.com?tag=bacon, which conveys a query string of tag with a value of bacon. 4. WordPress then converts the query specifi cation parameters into a MySQL database query to retrieve the content. The workhorse here is the get_posts() method within the WP_Query object that is described later in this chapter. The get_posts() method takes all of those query parameters and turns them into SQL statements, eventually invoking the SQL string on the MySQL database server and extracting the desired content. The content returned from the database is then saved in the WP_Query object to be used in the WordPress Loop and cached to speed up other references to the same posts made before another database query is executed.

c05.indd 74 12/6/12 1:28 AM Understanding the Loop ❘ 75

5. Once the content is retrieved, WordPress sets all of the is_ conditional tags such as is_home() and is_page(). These are set as part of executing the default query based on the URL parsing, and you’ll consider cases where you may need to reset these tags. 6. WordPress picks a template from your theme based on the type of query and the number of posts returned — for example, a single post or a category-only query — and the output of the query is passed to this default invocation of the Loop. The Loop can be customized for different website purposes. For example, a news site might use the Loop to display the latest news headlines. A business directory could use the Loop to display local businesses alphabetically by name, or always put posts about sponsoring businesses at the top of every displayed page. An e-commerce site might use the Loop to display products loaded into the website. The possibilities are endless when customizing the Loop in WordPress because it gives you complete control over what content is selected and the order in which it is rendered for display.

From Query Parameters to SQL Once the query parameters have been established, either by disassembling the URL provided by the reader or by having them explicitly set in a customized loop, the WP_Query object’s get_ posts() method translates those parameters into SQL for a database query. While you can exercise great control over the type, selection, and ordering of content through the query parameters, the WordPress core also exposes fi lters to allow you to change the generated SQL for even finer-grain control over content selection and grouping. The basic format of a SQL query is: SELECT fi elds FROM table WHERE conditions. “Fields” are the columns of the database that you want returned; you usually don’t need to modify this part of the query. The “conditions” specifi ed in the WHERE clause change the ordering, grouping, and number of posts returned. If you dump out the generated SQL query by examining the request fi eld of the WP_Query object, you’ll see that the WHERE portion of the SQL contains 1=1 as the fi rst conditional. If there are no other content selection parameters, the 1=1 ensures that the generated SQL isn’t syntactically malformed in the absence of other WHERE clauses; the SQL optimizer in MySQL knows enough to ignore the 1=1. “Table” is not simply the “posts” table in the MySQL database that contains all post data; it may also refer to an SQL JOIN of two or more tables where you need to select posts based on hierarchical metadata. WordPress makes it easy to put multiple tags on a post, or to put a post in more than one category, but relational databases aren’t adept at managing these hierarchical or networked relationships. As you see in Chapter 6, the WordPress data model uses multiple tables to manage these complex relationships, but that makes queries such as “fi nd all posts tagged bacon” more diffi cult to execute. For example, to select the posts tagged bacon, an SQL JOIN is needed to fi rst fi ndbacon in the metadata taxonomy, build an intermediate, in-memory table of posts that have been tagged with bacon, and then select posts whose IDs appear both in the intermediate table and the main WordPress content table. Database afi cionados call this a “Cartesian product” or inner join of two or more tables; the multiplicative description in both query complexity and memory consumption is accurate. In Chapter 8, you will dig into plugins and how they attach to fi lter and action hook insertion points in the WordPress core. Within the SQL request generation, there are a number of fi lters that are invoked to give plugin authors late-binding and very explicit control over the SQL that gets executed. For example, consider a plugin that changes the post selection based on custom post

c05.indd 75 12/6/12 1:28 AM 76 ❘ CHAPTER 5 THE LOOP

metadata and context that the plugin maintains in a separate database table. Your plugin would use the posts_join fi lter to rewrite the JOIN clause, adding another table and fi eld match clause to further expand the selection set. If you want to explore the core for the gory details of SQL generation, most of the query-to-request parsing is done in wp-includes/query.php, and the bulk of the JOIN work is set up in wp-includes/taxonomy.php. One fi nal note on SQL generation: WordPress does a very good job of building canonical URLs, that is, one and only one way to reference a particular post. Search engines notoriously consider http:// example.com/bacon and http://example.com/2012/bacon as distinct pages, even if they refer to the same piece of content (this is largely done to discourage more notorious practice of link farming where many distinct URLs are generated to feign the popularity of a single target). Part of the URL parsing function within the WordPress core attempts to clean up and redirect URLs to their canoni- cal form; the same functions also make every effort to return some relevant content rather than a 404 page. As a result, if an attempt to load a page by name fails to return any content, WordPress will insert a LIKE modifi er into the WHERE clause that contains the post name. For example, if a user supplies the URL http://example.com/2012/scott, but you have no posts with the title “Scott,” the LIKE clause will match any posts that start with “Scott,” such as “Scott Gomez Finally Scored.” Canonical URLs and “like name” matching are part of the complex maze of URL rewriting and intent parsing that try to generate a pleasant user experience, rather than an annoying 404 error.

Understanding Content in WordPress Before diving into the Loop in detail, it’s important to understand the different types of content in WordPress. By default, WordPress defi nes two types of content: posts and pages. What you’ll see in Chapter 6 is that all content types are stored in the same MySQL table, and are differentiated by their “type.” Since the release of WordPress 2.9, it’s possible to defi ne your own custom post types, which is basically custom content in WordPress. For example, you could have an Events custom post type to register events in WordPress. Throughout this chapter, content is referred to as “posts,” but it’s important to remember that posts could really be any type of content in WordPress. Custom post types are covered in Chapter 7.

Putting the Loop in Context Header The Loop is the heart of a theme, which is what controls how your content is displayed. It is the functional connection between the MySQL database data and the HTML that is rendered in the visitor’s browser. Basically, anywhere a post or page is displayed, WordPress is going to use the Loop. This can be a single post or page, a loop of posts, or a sequence of loops with different Sidebar display options.

Most WordPress themes feature a header, footer, and sidebar The WordPress Loop element. Figure 5-1 shows how the Loop is placed directly in the middle of these elements, creating your website content area. Footer This section of your website is usually dynamic and will change as you navigate through it. FIGURE 5-1: The WordPress Loop

c05.indd 76 12/6/12 1:28 AM Understanding the Loop ❘ 77

The Loop, by default, is used in your WordPress theme template Header fi les. Custom Loops can be created anywhere in your theme template fi les, as Figure 5-2 shows. Custom Loops are also used in plugins and widgets. Loops can be used anywhere inside of WordPress, but different methods exist for creating custom Sidebar Loops depending on where they are used, and the potential side effects of each construction will differ. Multiple Loops can be used throughout your theme template fi les. Custom Loops can be created in your header, sidebars, footer, and main content areas of your website. There is no limit to the number of Loops that can be displayed on your website. Keep in mind that a Loop is effectively a database query to select Footer content and then an iteration over the selection to display it. The default Loop uses context from the visited URL to make that selection but you can fi ne-tune and craft a query against the WordPress content database to implement any number of content FIGURE 5-2: Using multiple Loops management processes. The following section looks at the basic fl ow control of the Loop and the WordPress template functions provided to customize the way content is displayed while being handled inside of a loop. Having armed you with the basics, you will now go into building custom Loops based on hand-tailoring those query parameters.

Flow of the Loop The Loop uses some standard programming conditional statements to determine what and how to display. The fi rst statement in the Loop is an if statement that checks whether any posts exist, because you might not have any posts with the specifi ed category or tag. If content exists, the while statement is used to initiate the Loop and cycle through all posts or pages that need to be displayed. Finally, the_post() function is called to build the post data, making it accessible to other WordPress functions. Once the post data has been built, Loop content can be displayed in whatever format you like. Following is a minimal Loop example. This example features the only required elements for the Loop to function properly:

Remember that this is PHP code, so it needs to be surrounded in tags. This is the Loop in its simplest form. If you’re wondering how the output from the database query got handed to this simple Loop when there are no variables passed as parameters, the answer lies in the global

c05.indd 77 12/6/12 1:28 AM 78 ❘ CHAPTER 5 THE LOOP

variable $wp_query, which is an instance of WP_Query that is referenced by the functions in the simple Loop. It is, in effect, the default query for the Loop. Note that by the time this default Loop is called, WordPress has already called the get_posts() method within the default query object to build the list of appropriate content for the URL being viewed, and the Loop in this case is charged with displaying that list of posts. Later on, you look at how to hand-structure queries to exercise fi ne-grain control over post selection, but for now it’s safe to assume that the database heavy lifting has been done, and the results are stored in $wp_query, when the Loop is invoked. Some very minimal requirements exist for the Loop to work in WordPress. Let’s break down this example to look at the different parts of the Loop: if ( have_posts() ) : This line checks if any posts or pages are going to be displayed on the current page you are viewing. If posts or pages exist, the next line will execute: while ( have_posts() ) :

The preceding while statement starts the Loop, essentially looping through all posts and pages to be displayed on the page until there are no more. The Loop will continue while content exists to be displayed. Once all content has been displayed, the while loop will end. The have_posts() function simply checks to see if the list of posts being processed is exhausted, or had no entries to begin with. the_post();

Next, the the_post() function is called to load all of the post data. This function must be called inside your loop for the post data to be set correctly. Calling the_post() in turn calls the setup_postdata() function to set up the per-post metadata such as the author and tags of the content you are displaying in the Loop, as well as the content of the post itself. This data is assigned to a global variable each time through the Loop iteration. Specifi cally calling the_post() has the side effect of setting up the global $post variable used by most of the template tags described later on, and then advances to the next post in the list. Setting up the post data also applies the appropriate fi lters to the raw content that comes out of the WordPress database. WordPress stores user-edited content exactly as entered, so if a user adds a shortcode, for example, to add a Google AdSense item at the end of a post, the shortcode is stored in the database content. When the post setup is done, the plugin that converts that shortcode to a chunk of JavaScript is called, along with other registered plugins that modify the raw post content. You’ll look at the plugin mechanics in Chapter 8, but for now, it’s important to note the distinction between the raw post data in the WordPress query object and the fi ltered content that is eventually rendered. //loop content This is where all Loop template tags are placed and any additional code you want displayed inside the Loop. This is covered in more detail further along in this chapter. endwhile; endif;

The endwhile and endif calls end the Loop. Any code placed after these two lines will show at the bottom of your page, after all posts have been displayed. You could also place an else clause to dis- play a message if there is no content to display in the Loop.

c05.indd 78 12/6/12 1:28 AM Template Tags ❘ 79

The Loop is usually surrounded by HTML tags in your theme template fi les. The following code shows how the Loop is structured in the core Twenty Eleven theme that comes with WordPress:

Notice how the minimal Loop elements exist but are surrounded by HTML tags. This is how a normal theme template fi le will be structured to utilize the Loop. The HTML elements can certainly change, but the Loop elements stay the same. Customizing the style in which content is displayed and choosing post metadata to include in the page composition is done through template tags.

TEMPLATE TAGS

PHP functions used in your WordPress theme templates to display Loop content are called template tags. These tags are used to display specifi c pieces of data about your website and content. This allows you to customize how and where content is displayed on your website.

c05.indd 79 12/6/12 1:28 AM 80 ❘ CHAPTER 5 THE LOOP

For example, the the_title() template tag displays the title of your post or page inside the Loop. The major benefi t of using template tags is that you don’t need to know PHP code to use them. Many different template tags are available in WordPress. Some template tags must be inside the Loop, whereas other tags can be used anywhere in your theme template fi les. Note that in this context, template tags refer to the WordPress functions used to extract post data for display; template fi les are the theme elements that control how content for a particular content type is displayed. Put another way, template fi les contain Loops comprising template tags. For an updated list of template tags available in WordPress, visit http://codex.wordpress.org/Template_Tags.

Commonly Used Template Tags There is no shortage of template tags, but typically you will use only a handful of tags in your Loops. Following are the most commonly used template tags available in the Loop. These template tags will return and display the post data listed.

➤ the_permalink() — Displays the URL of your post. ➤ the_title() — Displays the title of the post. ➤ the_ID() — Displays the unique ID of your post. ➤ the_content() — Displays the full content of your post. ➤ the_excerpt() — Displays the excerpt of your post. If the Excerpt fi eld is fi lled out on the Post edit screen, that will be used. If not, WordPress will auto-generate a short excerpt from your post content. ➤ the_time() — Displays the date/time your post was published. ➤ the_author() — Displays the author of the post. ➤ the_tags() — Displays the tags attached to the post. ➤ the_category() — Displays the categories assigned to the post. ➤ edit_post_link() — Displays an edit link that is shown only if you are logged in and allowed to edit the post. ➤ comments_popup_link() — Displays a link to the comments form of your post. To learn how template tags work, just place any template tag inside the Loop and view the results. The following example views the values of a couple different template tags:

c05.indd 80 12/6/12 1:28 AM Customizing the Loop ❘ 81

As you can see, your post titles are displayed with links to the permalink for each post. The content of the post is displayed directly below the post title.

Tag Parameters Most template tags have parameters that can be added to modify the value returned. For example, the the_content() template tag has two parameters. The fi rst parameter allows you to set the more link text like so:

Your post content will be displayed as normal, but when the tag is found in your post, WordPress will automatically add the text Read more, which would link to the entire blog post. The second parameter determines whether to display the teaser paragraph again when viewing the full post. The default value is false so the teaser will be displayed in both places.

NOTE The more tag in WordPress allows you to display a defi ned teaser from the full post on your website. For example, you could display the fi rst paragraph of a post on your homepage, and only show the full blog post when a visitor clicks the link to view the full post. To accomplish this, you can place in your content in HTML view where you want this break to happen. In the visual edi- tor, there is a button to insert a More tag.

You can also send multiple parameters to any template tag that supports it. For example, the template tag the_title() accepts three parameters: $before, $after, and $echo. The following code sets the the_title() tags $before and $after parameters to wrap the post title with h1 tags: ', '' ); ?> You can also view the actual function in the WordPress source code. The post template functions are located in wp-includes/post-template.php. Doing a quick search for function the_ title() will lead you to the exact function for the the_title() tag. You can also use the Codex for a detailed description of the template tag you are working with, in this case http://codex.wordpress.org/Template_Tags/the_title.

CUSTOMIZING THE LOOP

The opening discussion of Loop fl ow of control mentioned that the main workhorse for data selection is the get_posts() method of the WP_Query object. In most cases, if you want to build a custom Loop, you’ll build your own WP_Query object and reference it explicitly. Alternatively, you can use the lower-level query_posts() and get_posts() functions (not to be confused with the methods within the WP_Query object of the same name) to manipulate the output of the default query that was passed into your Loop. Both query_posts() and get_posts() use the WP_Query class to retrieve content. You’ll look at the lower level approaches and discuss how and where you should — and shouldn’t — use them, but you’ll start with a discussion of how you build a custom query object.

c05.indd 81 12/6/12 1:28 AM 82 ❘ CHAPTER 5 THE LOOP

Using the WP_Query Object Once WordPress is handed a URL to parse by the web server, it goes to work disassembling the tokens in that URL and converting them into parameters for a database query. Here’s a bit more detail on what happens when manipulating your own WP_Query.

WP_Query is a class defi ned in WordPress that makes it easy to create your own custom Loops. Both query_posts() and get_posts() use the WP_Query class to retrieve the WordPress content. When you’re using query_posts(), the global variable $wp_query is used as an instance of WP_Query, making $wp_query the default data store for several operations. Custom Loops can be used anywhere in your theme template fi les to display different types of content; they must build on separate instances of a WP_Query variable.

When you create a new WP_Query object, it’s instantiated with some default functions for building queries, executing the query to get posts, and parsing parameters out of a URL. However, you can use these built-in object methods to construct your own parameter strings, creating custom loops that extract whatever particular content you need for that point in your Loop. The following is an example of a custom Loop displaying the fi ve most recent posts on your website: have_posts() ) : $myPosts->the_post(); ?>

Rather than using the simpler have_posts() and the_post() calls that you saw in the basic Loop, this custom loop calls the methods of the newly created WP_Query object $myPosts. The explicit invocation shown here and the default have_posts() call are functionally equivalent; have_posts(), for example, is merely calling $wp_query->have_posts() using the global query variable for the default query — that is, the one generated from parsing the URL handed to WordPress by the web server. Going into your default Loop from the URL used to invoke WordPress; there’s an additional step that takes the URL and parses it into an appropriate query string using the parse_query() method of the query object. When you build your own custom Loop, you explicitly set the parameters you want to control the query. Here’s a bit more detail on what happens inside the query function:

➤ Calling $myPosts->query() converts the parameters into an SQL statement via the func- tion $myPosts->get_posts(), which then executes the query against the MySQL database and extracts the content you’ve requested. ➤ Equally important, the query call sets up the conditional tags such as is_home() and is_ single() that are dependent upon the type of page displayed and the quantity of content for that page. ➤ The array of posts returned by the query is cached by WordPress so that future references to the same query won’t generate additional database traffi c. The key to building a powerful custom Loop is to map your content selection criteria into the right set of query parameters.

c05.indd 82 12/6/12 1:28 AM Customizing the Loop ❘ 83

Building a Custom Query Parameters are used to defi ne what content will be returned in your Loop, whether a custom Loop or altering the primary Loop. When creating Loops it’s essential to understand what parameters are available to help defi ne what content will be displayed. You can use many different, sometimes confusing, parameters in creating your custom Loop to alter the output of your content. Multiple parameters can also be set per query by separating the parameter name and values with an ampersand. For a detailed list of available parameters, visit http://codex.wordpress .org/Class_Reference/WP_Query#Parameters. The following sections cover some of the more commonly used parameters.

Post Parameters The most obvious, and sometimes most used parameters, select the number and types of posts to be displayed:

➤ p=2 — Loads an individual post by ID. ➤ name=my-slug — Loads post based on post slug (permalink tail). ➤ post_status=pending — Loads posts by post status. For example, if you choose to see only drafts, use post_status=draft. ➤ ignore_sticky_posts — Excludes sticky posts from being returned fi rst. A sticky post is one that always sorts to the top of the list of posts, independent of the other parameters set for the query. You can have multiple sticky posts, making them useful for calling attention to news announcements, highlighting changes, or otherwise grabbing reader’s attention, and this parameter lets you drop them from their priority slot at the top of the list. ➤ post_type=page — Loads posts based on type. If you only want to look at pages, not posts, post_type=page will retrieve them. This parameter enables special-purpose loops to select content based on custom post types, as you’ll see in Chapter 7. ➤ posts_per_page=5 — Number of posts to load per page. This is the default. To show all posts, set this parameter to –1. ➤ offset=1 — Number of posts to skip before loading.

Page Parameters Pages have parameters similar to those for posts to control their selection:

➤ page_id=5 — Loads an individual page by ID. Like post IDs and user IDs, page IDs can be found in the dashboard by hovering over a page and looking at the URL displayed at the bottom on your browser. ➤ pagename=Contact — Loads a page by name, in this case the Contact page. ➤ pagename=parent/child — Loads a child page by slug, or hierarchy of slugs (that is, its path).

c05.indd 83 12/6/12 1:28 AM 84 ❘ CHAPTER 5 THE LOOP

Category, Tag, and Author Parameters Posts can also be sorted by the category into which they were placed, by tags applied to the post, or by author information:

➤ cat=3,4,5 — Load posts based on category ID. ➤ category_name=About Us — Loads posts based on category name. Note that if a post belongs to more than one category, it will show up in selections for each of those categories. ➤ tag=writing — Loads posts based on tag name. ➤ tag_id=34 — Loads posts based on tag ID. ➤ author=1 — Loads posts based on author ID. ➤ author_name=brad — Loads posts based on author’s name.

Time, Date, Ordering, and Custom Parameters Parameters to select content based on their chronology are a key part of building an archive of posts, or providing a view into content through a calendar on your blog’s homepage. You can also change the sort parameter and the sort order. If you’re building an online index, and want to show an alphabetical post listing, you’ll set the parameters for querying posts by month and author, but order the results by title.

➤ monthnum=6 — Loads posts created in June. ➤ day=9 — Loads posts created on the 9th day of the month. ➤ year=2012 — Loads posts created in 2012. ➤ orderby=title — Field to order posts by. ➤ order=ASC — Defi nes ascending or descending order of orderby. ➤ meta_key=color — Loads posts by custom fi eld name. Refer to the custom taxonomy and data discussion in Chapter 7 to see how custom fi elds are added to posts. ➤ meta_value=blue — Loads posts by custom fi eld value. Must be used in conjunction with the meta_key parameter above.

Putting It Together Now look at some examples using parameters. The following examples use the $myPosts- >query() function from the $myPosts custom query object created in the example to select the con- tent displayed in your custom Loop. Display post based on post ID: $myPosts = new WP_Query( 'p=1' ); Display the fi ve latest posts, skipping the fi rst post: $myPosts = new WP_Query( 'posts_per_page=5&offset=1' );

c05.indd 84 12/6/12 1:28 AM Customizing the Loop ❘ 85

Display all posts from today: // display all posts from the current date $today = getdate(); // get todays date $myPosts = new WP_Query('year=' .$today["year"] .'&monthnum=' .$today["mon"] .'&day=' .$today["mday"] ); Display all posts from October 31, 2013: $myPosts = new WP_Query( 'monthnum=10&day=31&year=2013' );

Display all posts from category ID 5 with the bacon tag: $myPosts = new WP_Query( 'cat=5&tag=bacon' );

Display all posts with the bacon tag, excluding posts in category ID 5: $myPosts = new WP_Query( 'cat=-5&tag=bacon' );

Display all posts with the tag writing or reading: $myPosts = new WP_Query( 'tag=writing,reading' );

Display all posts with the tags writing and reading and tv: $myPosts = new WP_Query( 'tag=writing+reading+tv' );

Display all posts with a custom fi eld named color with a value of blue: $myPosts = new WP_Query( 'meta_key=color&meta_value=blue' ); Adding Paging to a Loop If your custom Loop requires paging (navigation links), you will need to take a few extra steps. Paging is currently designed to work only with the $wp_query global variable; that is, it works within the default Loop and requires some sleight of hand to make it work in custom Loops. You need to trick WordPress into thinking your custom query is actually $wp_query in order for paging to work. have_posts() ) : $wp_query->the_post(); ?>

First, you have to store the original $wp_query variable into the temporary variable $temp. Next, you set $wp_query to null to completely fl ush it clean. This is one of the few times it’s acceptable to overwrite a global variable value in WordPress. Now set your new WP_Query object into the $wp_query variable and execute it by calling the object’s query() function to select posts for your custom Loop. Notice the $paged variable added to the end of the query. This stores the current

c05.indd 85 12/6/12 1:28 AM 86 ❘ CHAPTER 5 THE LOOP

page, using the get_query_var() function, so WordPress knows how to display the navigation links. Now display your navigation links for paging:

Finally, you need to reset $wp_query back to its original value: Now your custom Loop will contain proper pagination based on the content returned.

Using query_posts( ) A tremendous amount of customization can be done by specifying the appropriate set of parameters for your Loop. While the WP_Query object is the most general-purpose mechanism for extracting content from the WordPress database, there are other lower-level methods that you’ll encounter.

The query_posts() function is used to easily modify the content returned for the default WordPress Loop. Specifi cally, you can modify the content returned in $wp_query after the default database query has executed, fi ne-tune the query parameters, and re-execute the query using query_posts(). The downside to calling query_posts() in this fashion is that the previously cached results from the default query are discarded, so you’re incurring a database performance hit to use this shortcut. The query_posts() function should be placed directly above the start of the Loop: query_posts( 'posts_per_page=5&paged='.$paged ); if ( have_posts() ) : while ( have_posts() ) : the_post(); //loop content (template tags, html, etc) endwhile; endif; This example tells WordPress to display only fi ve posts.

Explicitly calling query_posts() overwrites the original post content extracted for the Loop. This means any content you were expecting to be returned before using query_posts() will not be returned. For example, if the URL passed to WordPress is for a category page at http://example .com/category/zombie/, none of the zombie category posts will be in the post list after query_ posts() has been called unless one is in the fi ve most recent posts. You explicitly overwrite the query parameters established by the URL parsing and default processing when you pass the query string to query_posts(). To avoid losing your original Loop content, you can save the parsed query parameters by using the $query_string global variable: // initialize the global query_string variable global $query_string;

// keep original Loop content and change the sort order query_posts( $query_string . “&orderby=title&order=ASC” );

c05.indd 86 12/6/12 1:28 AM Customizing the Loop ❘ 87

In the preceding example, you would still see all of your zombie category posts, but they would be ordered alphabetically by ascending title. This technique is used to modify the original Loop content without losing that original content.

You can also pack all of your query_posts() parameters in an array, making it easier to manage. Following is an example of how to retrieve only the sticky post set in WordPress using an array called $args to store the parameter values: $args = array( 'posts_per_page' => 1, 'post__in' => get_option( 'sticky_posts' ) ); query_posts( $args );

If no sticky post is found, the latest post will be returned instead. The query_posts() function is used to modify the main page Loop only. It is not intended to create additional custom Loops. If you want to make a slight change to the default query — for example, adding posts of a specifi c category or tag to every displayed page — then the query_posts() approach is a shortcut. However, it’s not without side effects or cautions:

➤ query_posts() modifi es the global variable $wp_query and has other side effects. It should not be called more than once and shouldn’t be used inside the Loop. The example shows the call to query_posts() before post processing has started, when the extra parameters are added to the query string but before the Loop has begun to step through the returned post list. Calling query_posts() more than once, or inside the Loop itself, can result in your main Loop being incorrect and displaying unintended content. ➤ query_posts() unsets the global $wp_query object, and in doing so, may invalidate the val- ues of conditional tags such as is_page() or is_home(). Going through the entire WP_Query object instantiation sets all of the conditional tags appropriately. For example, you may fi nd with the shortcut that you have added content to a selection that the default query found contained only one post, and therefore is_single() is no longer valid. ➤ Calling query_posts() executes another database query, invalidating all of the cached results from the fi rst, default query. You at least double the number of database queries executed and are incurring a performance hit for each trip back to MySQL; on the other hand the default query has already been run by the time you get to the default Loop, so there’s little chance to work around it if you’re building an entirely custom main Loop. Using get_posts( ) Like query_posts(), there’s an alternative, simpler access function called get_posts() that retrieves raw post data. You’ll see get_posts() used in administration pages to generate a list of pages of a particular type, or it may be used within a plugin to grab all raw data for a set of posts and examine it for patterns such as common terms, tags, or external links, with the intent of discarding the content after a quick digestion. It’s not intended for user-facing content display because it turns off much of query processing and fi ltering that is done within the more general WP_Query approach.

What get_posts() lacks, specifi cally, is the ability to set up all of the global data needed to make template tags refl ect the current post data. One main issue is that not all template tags are available

c05.indd 87 12/6/12 1:28 AM 88 ❘ CHAPTER 5 THE LOOP

to get_posts() by default. To fi x this defi ciency, you need to call the setup_postdata() function to populate the template tags for use in your Loop. The following example shows how to retrieve a single random post using get_posts():

$randompost = get_posts( 'numberposts=1&orderby=rand' ); foreach( $randompost as $post ) : setup_postdata( $post ); ?>

You’ll notice another major difference using get_posts() — the value returned is an array. The foreach loop code is used to cycle through the array values. This example returns only one post, but if more than one were returned, this would cycle through each. Then the setup_post- data() function is called to populate the data for your template tags.

Remember that you can also set up your get_posts() parameter using an array: 1, 'orderby' => rand );

$randompost = get_posts( $args );

Although you may see older code using get_posts() or query_posts() constructions, WP_Query is the preferred approach and should be the heart of custom loop syntax. However, there are times when you’ll want the quick-and-dirty access provided by get_posts() to generate additional con- text or data for further customization of your Loop or in a plugin.

Resetting a Query When customizing the main Loop, or creating custom Loops, it’s a good idea to reset the Loop data after you are done. WordPress features two different functions to handle this: wp_reset_postdata() and wp_reset_query().

The fi rst method for resetting post data is wp_reset_data(). This function actually restores the global $post variable to the current post in the main query. This is the preferred method when using WP_Query to create custom Loops.

For example, assume you have the following custom Loop in your theme’s header.php fi le:

// The Loop while ( $myPosts->have_posts() ) : $myPosts->the_post(); ?>

c05.indd 88 12/6/12 1:28 AM Customizing the Loop ❘ 89

This will display a random post in the header of your theme. This code will also change the main query object for other Loops on the page. The original query data will not be available, which could produce unexpected results on the main posts Loop for your theme.

To fi x the problem, place a call to wp_reset_postdata() directly after your custom Loop like so: $myPosts = new WP_Query( 'posts_per_page=1&orderby=rand' );

// The Loop while ( $myPosts->have_posts() ) : $myPosts->the_post(); ?>

// Reset Post Data wp_reset_postdata();

Calling this function will restore the $post variable to the current post in the query. This will eliminate any strangeness in the main query for the page you are viewing.

The second method available for resetting post data is the wp_reset_query() function. From time to time, you may run into problems with page-level conditional tags being used after a custom Loop has been created. Conditional tags allow you to run different code on different pages in WordPress, for example, using the conditional tag is_home() to determine if you are viewing the main blog page. This problem is caused, as indicated in the, “Using query_posts( )” section, by potentially changing the output of a database query after setting the conditional tags based on its original set of values. To fi x this issue, you need to call wp_reset_query(). This function will properly restore the original query, including the conditional tags set up early in the URL parsing process. Consider the following example:

Executing this code will return the latest fi ve posts followed by the links saved in your WordPress link manager. The problem you will run into is that the is_home() conditional tag will not be interpreted correctly, meaning your links will show on every page, not just the homepage. To fi x this issue, you need to include wp_reset_query() directly below your Loop:

c05.indd 89 12/6/12 1:28 AM 90 ❘ CHAPTER 5 THE LOOP

if( is_home() && !is_paged() ): wp_list_bookmarks( 'title_li=&categorize=0' ); endif; ?>

Now that you have properly restored your Loop’s instance of the WP_Query object, the conditional tag is_home() will be followed and your links will now display only on the homepage of your website. It’s a good practice to add wp_reset_query() after using query_posts() in your Loop to ensure you do not run into problems down the road. The wp_reset_query() function actually calls wp_reset_postdata(), but it does one additional step. The function actually destroys the previous query before resetting it. In short, wp_reset_query() should always be used after a query_posts() Loop and wp_reset_postdata() should be used after a WP_Query or get_posts() custom Loop.

More Than One Loop The Loop can be used multiple times throughout your theme and plugins. This makes it easy to display different types of content in multiple places throughout your WordPress website. Maybe you want to display your most recent blog posts below each page on your website. You can achieve this by creating more complex Loops that make multiple passes through the list of posts, or by generating multiple post arrays over which to loop.

Nested Loops Nested Loops can be created inside your theme templates using a combination of the main Loop and separate WP_Query instances. For example, you can create a nested Loop to display related posts based on post tags. The following is an example of creating a nested Loop inside the main Loop to display related posts based on tags:

//loop content (template tags, html, etc) ?>

$tags = wp_get_post_terms( get_the_ID() ); if ( $tags ) {

echo 'Related Posts';

$tagcount = count( $tags ); for ( $i = 0; $i < $tagcount; $i++ ) { $tagIDs[$i] = $tags[$i]->term_id; }

$args=array(

c05.indd 90 12/6/12 1:28 AM Customizing the Loop ❘ 91

'tag__in' => $tagIDs, 'post__not_in' => array( $post->ID ), 'posts_per_page' => 5, 'ignore_sticky_posts' => 1 ); $relatedPosts = new WP_Query( $args ); if( $relatedPosts->have_posts() ) { //loop through related posts based on the tag while ( $relatedPosts->have_posts() ) : $relatedPosts->the_post(); ?>

} endwhile; endif; ?> This code will display all of your posts as normal. Inside the main Loop, you check if any other posts contain any of the same tags as your main post. If so, you display the latest fi ve posts that match as related posts. If no posts match, the related posts section will not be displayed.

Multi-Pass Loops The rewind_posts() function is used to reset the post query and loop counter, allowing you to do another Loop using the same content as the fi rst Loop. Place this function call directly after you fi nish your fi rst Loop. Here’s an example that processes the main Loop content twice:

Advanced Queries You can also perform more advanced queries in your Loops. Now construct a Loop that will com- pare a custom fi eld value using the meta_compare parameter: $args = array( 'posts_per_page' => '-1', 'post_type' => 'product', 'meta_key' => 'price', 'meta_value' => '13', 'meta_compare' => '<='

c05.indd 91 12/6/12 1:28 AM 92 ❘ CHAPTER 5 THE LOOP

);

$myProducts = new WP_Query( $args );

// The Loop while ( $myProducts->have_posts() ) : $myProducts->the_post(); ?>

// Reset Post Data wp_reset_postdata();

As you can see, the meta_compare parameter is used to display all products with a meta value for price that is less than or equal to (<=) 13. The meta_compare parameter can accept all sorts of com- parison operators such as !=, >, >=, <, <=, and the default, which is =.

For more complex meta data queries, you’ll use the meta_query parameter. Now you can expand upon the preceding example. Instead of just returning product entries that are less than or equal to a price of 13, you can also only return products that are the color blue: $args = array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => 'color', 'value' => 'blue', 'compare' => '=' ), array( 'key' => 'price', 'value' => '13', 'type' => 'numeric', 'compare' => '<=' ) ) );

$myProducts = new WP_Query( $args );

// The Loop while ( $myProducts->have_posts() ) : $myProducts->the_post(); ?>

// Reset Post Data wp_reset_postdata();

Notice the meta_query parameter accepts an array of parameters. In this example, the fi rst item in the array is an array to verify the products are blue. The second parameter is an array to verify the product price is less than or equal to 13. Creating Loops using meta query parameters can be extremely powerful. This is an important tool for creating complex websites with various meta data options.

c05.indd 92 12/6/12 1:28 AM Global Variables ❘ 93

GLOBAL VARIABLES

A global variable is a variable that has a defi ned value that can be accessed anywhere within the WordPress execution environment. These variables store all types of information about the Loop content, author, and users, and specifi c information about the WordPress installation such as how to connect to the MySQL database. Global variables should only be used to retrieve data, meaning you should never write data to these variables directly. Overwriting the global variable values could cause unexpected results in WordPress because signifi cant parts of core and extended functionality depend on these values being set within one context and remaining consistent for the duration of a query, page load, or single-post handling. Assigning values to global variables almost always has unintended side effects, and they’re almost always not what the user or blog author wanted. However, global variables are discussed here to shed more light on how post data can be manipulated, and you may see code snippets that utilize these functions for post processing outside of the Loop.

Post Data You saw how the key fi rst step in the Loop is calling the_post(). Once invoked, you will have access to all of the data in WordPress specifi c to the post being displayed. This data is stored in the global $post variable. The $post variable stores the post data of the last post displayed on the page. So if your Loop displays ten posts, the $post variable will store post data for the tenth post displayed.

The following example shows how you can reference the $post global variable and display all values in the array using the print_r() PHP function.

The preceding code will print the array values for the $post global variable. The default WordPress blog post would look like this: stdClass Object ( [ID] => 1 [post_author] => 1 [post_date] => 2012-06-09 19:05:19 [post_date_gmt] => 2012-06-09 17:23:50 [post_content] => Welcome to WordPress. This is your first post. Edit or delete it, then start blogging! [post_title] => Hello world! [post_excerpt] => [post_status] => publish [comment_status] => open [ping_status] => open [post_password] => [post_name] => hello-world [to_ping] => [pinged] =>

c05.indd 93 12/6/12 1:28 AM 94 ❘ CHAPTER 5 THE LOOP

[post_modified] => 2012-06-09 19:04:12 [post_modified_gmt] => 2012-06-09 19:04:12 [post_content_filtered] => [post_parent] => 0 [guid] => http://localhost/Brad/?p=1 [menu_order] => 0 [post_type] => post [post_mime_type] => [comment_count] => 1 [ancestors] => Array ( )

[filter] => raw )

As you can see, the $post global variable contains all sorts of data for the post. You can also display specifi c pieces of data from the array, such as the post title and content like so: post_title; //display the post title echo $post->post_content; //display the post content ?>

Accessing the content through the global $post variable means that you are accessing the unfi ltered content. This means any plugins that would normally alter the output of the content will not affect the global content value. For example, if you had the built-in [gallery] shortcode in your post to display all images uploaded on the post, retrieving the post content as shown would return [gallery] instead of the actual image gallery. Remember that WordPress provides template tags that can be called anywhere to retrieve these values as well, and in most cases, template tags are going to be the preferred mechanism for getting at these bits. For example, if you need to get the permalink of your post, you can use the following method: ID ); //displays the posts permalink ?> This is covered in more detail in the section, “Working Outside the Loop,” later in this chapter.

Author Data $authordata is a global variable that stores information about the author of the post being dis- played. You can use this global variable to display the author’s name: display_name; ?>

c05.indd 94 12/6/12 1:28 AM Global Variables ❘ 95

The $authordata variable is created when setup_postdata() is called during the_post() function call in the Loop. This means the $authordata global variable will not be created until the Loop has run for the fi rst time. Another problem with this method is that the global values do not get passed through hook fi lters, meaning that any plugin you install to override this functionality would not be run. The preferred method for accessing the author metadata, like that for getting post data, is to use the available WordPress template tags. For example, to display the author’s display name, you would use this code:

The get_the_author_meta() and the_author_meta() functions are available for retrieving all metadata related to the author of the content. If this template tag is used inside the Loop, there is no need to pass the user ID parameter. If used outside of the Loop, the user ID is required to determine what author metadata to retrieve.

User Data The $current_user global variable stores information on the currently logged-in user. This is the account that you are currently logged in to WordPress with. Following is an example showing how to display the logged-in user’s display name: display_name; ?> This is a useful technique if you want to display a welcome message to your users. Remember that the display name will default to the user’s username. To display a welcome message to any user that is logged in, you could use this code: display_name ) { echo 'Welcome ' .$current_user->display_name; } ?> Environmental Data WordPress also has global variables created for browser detection. The following is an example showing how you can detect the user’s browser version in WordPress using global variables:

if ( $is_lynx ) { echo "You are using Lynx"; }elseif ( $is_gecko ) { echo "You are using Firefox";

c05.indd 95 12/6/12 1:28 AM 96 ❘ CHAPTER 5 THE LOOP

}elseif ( $is_IE ) { echo "You are using Internet Explorer"; }elseif ( $is_opera ) { echo "You are using Opera"; }elseif ( $is_NS4 ) { echo "You are using Netscape"; }elseif ( $is_safari ) { echo "You are using Safari"; }elseif ( $is_chrome ) { echo "You are using Chrome"; }elseif ( $is_iphone ) { echo "You are using an iPhone"; } ?> This is extremely useful when designing a website that needs to include browser-specifi c tasks or functionality. As always, it’s best to stick with web standards and degrade gracefully for lesser browsers, but in some circumstances this can be very benefi cial. For example, you can use the $is_iphone variable to load a custom style sheet for iPhone web users. WordPress features another global variable to detect if the user is on a mobile device, which could be a smartphone or tablet. This global variable is called $is_mobile. Rather than calling this global variable directly, there’s a handy function available called wp_is_mobile(). This function detects if the user is on a mobile device. If you are browsing using a mobile device, the function returns true; if not, the function returns false, as shown here: if ( wp_is_mobile() ) { echo "You are viewing this website on a mobile device"; }else{ echo "You are not on a mobile device"; }

WordPress also stores what type of web server the website is hosted on using the $is_IIS and $is_apache global variables. Here’s an example: Depending what web server a website is using, code can produce different results than expected. As a developer, you need to consider that your plugins and themes may be running on WordPress installations on different web servers; you might also need to check what the user is running in order to accomplish specifi c tasks.

Global Variables or Template Tags? Generally speaking, template tags should be used whenever they can be. There will be certain instances where a template tag will not be available. In this case, global variables can be substituted to access the information you need. Also, global variables are great for retrieving unfi ltered data,

c05.indd 96 12/6/12 1:28 AM Working Outside the Loop ❘ 97

meaning the values will bypass any plugin, altering what would normally be used against the con- tent and giving you the original value to work with. Once your code has accessed or processed the original value, you can still force the plugin fi lters to run using the following code: post_content );?> While this is included in a discussion of working outside of the Loop, you can access these global variables inside the loop, but again remember to treat globals as read-only, as changing their values will have possibly negative side effects.

WORKING OUTSIDE THE LOOP

There are times when you’ll want to access generic post information, or to manipulate some infor- mation about the currently displayed post outside of the Loop. WordPress provides some functions to operate on sets of posts for even fi ner-grain control over post display. Along with access to global variables, there is a set of WordPress functions to return generic infor- mation that’s not specifi c to a single post, or the post currently displayed. Following is a list of fre- quently used functions when working outside the Loop:

➤ wp_list_pages() — Displays a list of pages as links ➤ wp_list_categories() — Displays a list of categories as links ➤ wp_list_bookmarks() — Displays links saved in the Links SubPanel ➤ wp_tag_cloud() — Displays a tag cloud from all tags ➤ get_permalink() — Returns the permalink of a post ➤ next_posts_link() — Link to display previous posts ➤ previous_posts_link() — Link to display next posts You already saw how you could create navigational links using next_posts_link() and previous_posts_link() in the custom Loop example. Now explore some of these functions to get a real feel for how they work.

To display a list of pages in WordPress, you can use the wp_list_pages() function. This function will return your pages in a list format, so it’s important to wrap the function call with

    tags, as shown here:
    The preceding code would generate a list of pages from WordPress with links. Notice that you set the parameter title_li to nothing, which eliminates the default title displayed for your pages. The function would generate your menu list like so:

    You can also use the wp_page_menu() function to generate a page menu. There are several advantages to this page listing function. The fi rst is a new show_home parameter allowing a Home link to automatically be added to the list of pages. You also don’t have to remove the title using title_li as done in the preceding code. This function also wraps a custom

    around your menu, the class of which you can set. The following is an example of this function:

    Another common function for generating links is wp_list_categories(). This function lists your categories, and subcategories, in a list as well. Consider the following example:

    This code will generate a list of categories with links. As before, you are setting your title to noth- ing, rather than the default Categories title. You are also setting the depth to 4. The depth parameter controls how many levels in the hierarchy of categories to be included in the list. The categories will be ordered by their name. You are also excluding three categories (8, 16, and 34) based on their IDs.

    The functions next_posts_link() and previous_posts_link() are typically used directly after your Loop has completed. These two functions will generate the previous and next links for view- ing more posts on your website. Notice that the next_posts_link() function actually returns your previous posts. The reason for this is that WordPress assumes your posts are displaying in reverse chronological order, meaning the next page of posts would actually be posts from earlier in the timeline. Now imagine you’d like to load a single post outside of the Loop. To do this, you use the get_post() function to load your post data. The following example loads the post data for post ID 1031: post_title .'
    '; echo 'Post Content: ' .$myPost->post_content .'
    '; ?>

    The get_post() function has only one required parameter: the post ID you want to load. You must pass a variable containing an integer for the ID. Passing a literal integer (for example, 5) will cause a fatal error. The second optional parameter is how you would like the results returned: as an object, an associative array, or a numeric array. By default, an object is returned. To return an associative array you can run this code:

    c05.indd 98 12/6/12 1:28 AM Working Outside the Loop ❘ 99

    '; echo 'Post Content: ' .$myPost['post_content'] .'
    '; ?>

    No matter how you return the results, however, this invocation of get_post() returns the raw content from the WordPress database. Filters and processing normally done within the loop won’t be applied to the returned content. The solution is to use the setup_postdata() function in conjunction with get_post() to set up your global post data and template tags for use with your post:

    The get_post() function uses the internal WordPress object cache. This means that if the post you are loading is already in the cache you will avoid running an unneeded database query. It’s easy to see how useful this function can be to quickly and effi ciently load a single post outside of the Loop. Some functions that can be used inside the Loop can also be used outside of the Loop. For example, you can use the the_author_meta() function to retrieve specifi c author metadata: The email address for user id 1 is

    Remember that when calling the the_author_meta() function outside of the Loop, you have to specify the author’s ID that you want to load metadata for. If you call this function inside the Loop, you do not need to specify this ID because it will load the author data for the current post. WordPress also features specifi c functions for retrieving individual data about a post outside of the Loop. For example, you can use the get_the_title() function to retrieve a post’s title based on post ID like so: You can also use a function to retrieve post metadata (custom fi elds) from an individual post. To do this, you use the get_post_meta() function, as shown here:

    The get_post_meta() function accepts three parameters: post ID, key, and single. The post ID is the ID of the post you want to load metadata for. The key is the name of the meta value you want to load. The third optional value determines whether the results are returned as an array or whether the function will return a single result. By default, this is set to false so an array would be returned. As you can see, you can set this value to true so only a single color is returned.

    c05.indd 99 12/6/12 1:28 AM 100 ❘ CHAPTER 5 THE LOOP

    SUMMARY

    This chapter covered the basic mechanics of WordPress content selection and display and provided a guide to the WordPress core to help you locate the code used to implement these functions. The real power of WordPress is in its extensibility through plugins and themes. You are fi rst going to look at the WordPress data model in more detail in Chapter 6, showing you how the various data items saved for all content, users, and metadata relate to each other. Chapter 7 will cover custom post types, custom taxonomies, and metadata showing you the various types of content you can defi ne and use in WordPress. You will then use that as the basis for a full-fl edged plugin construction discussion in Chapter 8. Along with plugins, themes are the other primary avenue for extending and customizing WordPress, and you reapply some of the Loop constructs with a deeper look at templates and content presentation in Chapter 9.

    c05.indd 100 12/6/12 1:28 AM 6 Data Management

    WHAT’S IN THIS CHAPTER?

    ➤ Understanding the WordPress database ➤ Learning about database table relationships ➤ Working with the WordPress database class ➤ Debugging custom queries

    Almost every website on the Internet today is connected to a database that stores informa- tion about that website. WordPress is no different and is powered by a MySQL database back end. This database stores all of the data for your website, including your content, users, links, metadata, settings, and more. This chapter covers how data is stored, what data is stored, and how to work with that data in WordPress to help you build amazing websites.

    DATABASE SCHEMA

    The default installation of WordPress contains 11 database tables. WordPress prides itself on being very lightweight and the database is the foundation for this. The database struc- ture is designed to be very minimal yet allow for endless fl exibility when developing and designing for WordPress. To understand the database schema, it helps to view a database diagram. Figure 6-1 shows an overview of the WordPress database structure and the tables created dur- ing a standard WordPress installation. Keep in mind that plugins and themes have the ability to create custom tables. WordPress Multisite also creates additional tables so your WordPress database may contain more tables than just the default WordPress tables.

    c06.indd 101 12/6/12 1:29 AM 102 ❘ CHAPTER 6 DATA MANAGEMENT

    FIGURE 6-1: WordPress database diagram

    When a new major release of WordPress is launched, a few database changes are usually made. These changes are usually very minor, such as changing a table fi eld data type or removing a fi eld that is no longer in use. Backward compatibility is a major focus for the WordPress devel- opment community so any changes made to the database are highly scrutinized and will rarely affect active plugins and themes. The Codex features a very thorough database changelog you can reference when a new version of WordPress is released: http://codex.wordpress.org/ Database_Description#Changelog. The table structure in WordPress is very consistent. Each table in your database contains a unique ID fi eld, which is the primary key of the table. Each table also contains one or more indexes on fi elds, which improves the speed of data retrieval when executing queries against the data. As you saw in Chapter 5, each trip through the Loop in a theme is going to generate at least one, and per- haps several, queries to extract posts, pages, and their related metadata or comments.

    c06.indd 102 12/6/12 1:29 AM Table Details ❘ 103

    The most important fi eld in every table is the unique ID fi eld. This fi eld is not always named ID, but is an auto-incrementing fi eld used to give each record in the table a unique identifi er. For example, when you fi rst install WordPress, a default post is created titled “Hello world!” Because this is the fi rst post created in thewp_posts table, the ID for this post is 1. Each post is given a unique ID that can be used to load post-specifi c information and can also be used as the joining fi eld against other tables in the database. There is one caveat to this, and that is post revisions, attachments, and custom post types. Each one of these entries is saved as a new record in the wp_posts table so they each get their own unique ID, which means your published post IDs may not be sequential. For example, your fi rst post may have an ID of 1, whereas your second post may have an ID of 15. It all depends on how many additional entries have been created between each post.

    TABLE DETAILS

    Currently, 11 database tables have been created for WordPress. Following is a list of those tables and details on what data they store:

    ➤ wp_commentmeta — Contains all metadata for comments. ➤ wp_comments — Contains all comments within WordPress. Individual comments are linked back to posts through a post ID. ➤ wp_links — Contains all links added via the Link Manager section. ➤ wp_options — Stores all website options defi ned under the Settings SubPanel. Also stores plugin options, active plugins and themes, and more. ➤ wp_postmeta — Contains all post metadata (custom fi elds). ➤ wp_posts — Contains posts of all types (default and custom post types), pages, media records, and revisions. Under most circumstances, this is the largest table in the database. ➤ wp_terms — Contains all taxonomy terms defi ned for your website, mapping their text descriptions to term numbers that can be used as unique indexes into other tables. ➤ wp_term_relationships — Joins taxonomy terms with content, providing a membership table. It maps a term such as a tag or category name to the page or post that references it. ➤ wp_term_taxonomy — Defi nes the taxonomy to which each term is assigned. This table allows you to have categories and tags with the same name, placing them in different named taxonomies. ➤ wp_users — Contains all users created in your website (login, password, e-mail). ➤ wp_usermeta — Contains metadata for users (fi rst/last name, nickname, user level, and so on).

    Each database table has a specifi c purpose within WordPress. The next section breaks down some of the more common tables and looks at some examples of working with each.

    c06.indd 103 12/6/12 1:29 AM 104 ❘ CHAPTER 6 DATA MANAGEMENT

    WordPress Content Tables To retrieve all of your website content, you’ll be accessing the wp_posts table. This table stores all of your posts, pages, attachments, and revisions. Attachment records are stored in this table, but the actual attachments are not. They are physically stored on your hosting server as a standard fi le. The following SQL query is an example of how to extract all of your posts from the database, and is the short form of what happens in the default WordPress Loop:

    SELECT * FROM wp_posts WHERE post_type = 'post' AND post_status = 'publish' ORDER BY post_date DESC

    This query selects all records from wp_posts with a post_type of 'post'. The post_type fi eld designates what type of content you are viewing. To return all pages, just change that value to 'page'. In this example, you want published posts only, so make sure post_status is set to 'publish'. You are also ordering your table records by post_date descending, so your posts will be displayed in reverse chronological order. Querying data and what tools are available to help you do so are discussed later in this chapter.

    Let’s explore some of the more useful fi elds in the wp_posts table. You already know your ID fi eld contains your post’s unique ID. The post_author fi eld is the unique ID of the author of the post. You can use this to retrieve author-specifi c data from thewp_users table. The post_date is the date the post was created. The post_content fi eld stores the main content of your post or page and post_title is the title of that content.

    One very important fi eld is the post_status fi eld. Currently, seven different post statuses are defi ned in WordPress:

    1. publish — A published post or page. 2. inherit — A post revision. 3. pending — Post that is pending review by an administrator or editor. 4. private — A private post. 5. future — A post scheduled to publish at a future date and time. 6. draft — A post still being created and is a draft. 7. trash — Content is in the trash bin and can still be recovered.

    Post status comes into play when contributor roles are used to limit a post creator’s ability to post or edit existing content. The use of roles is discussed in Chapter 12, and their impact on content man- agement workfl ow is discussed in Chapter 14. As with almost everything in WordPress, custom post statuses can be created by plugins and themes.

    The post_type is also stored in this table. This value is what distinguishes different types of content in WordPress: posts, pages, revisions, and attachments. Since the release of WordPress 2.9, custom post types can be created, which opens the door to endless possibilities when defi ning custom con- tent in WordPress.

    c06.indd 104 12/6/12 1:29 AM Table Details ❘ 105

    The wp_users table contains data for your registered member accounts. Again, you see the ID fi eld indicating the unique identifi er for user records. The user_login is the username of the user. This is the value the user must enter when logging in to WordPress. The user_pass fi eld contains the phpass encrypted user password. The registered user’s e-mail is stored in the user_email fi eld. The user_url fi eld contains the member’s website and the user registration date is saved in user_registered.

    Next you will explore the wp_comments table. This table stores all of the comments, pingbacks, and trackbacks for your website.

    Viewing the comment records, you’ll notice the ID fi eld is named comment_ID. Even though this fi eld is not named ID, it is still the unique identifi er for this record in the table. The comment_post_ ID is the unique ID of the post the comment was added to. Remember that by default you don’t have to be logged in to make comments in WordPress. For this reason, you’ll see similar fi elds as in your users table.

    The comment_author fi eld stores the name of the commenter. If the comment is a pingback or track- back, it will contain the name of the post that sent the ping. The comment_author_email contains the commenter’s e-mail address, and his or her website is stored in comment_author_url. Another important fi eld is the comment_date, which is the date the comment was created. This fi eld is used to display your post comments in the correct order.

    WordPress Taxonomy Tables Terms, relationships, and taxonomies are broken into three distinct tables to allow many-to-one relationships between categories, tags, items in custom taxonomies, and posts. These relationships are hierarchical and multi-valued. While you could add an array of tag or category identifi ers to each row in the wp_posts table, for example, that approach puts an explicit limit on the number of descriptive relationships for each post while also wasting space allocated for tags or categories that may not be assigned. If you create a category called “cured ham,” and put four posts in that category, all three taxonomy- related tables are updated:

    1. One row in the wp_terms table defi nes “cured ham” and its slug, or diminutive form used in URLs. This relationship gets a unique identifi er (key) useful for matching the term to other tables. 2. One row in the wp_term_taxonomy table maps “cured ham” to the “category” taxonomy. This relationship also gets a unique key, representing the combination of “cured ham” in “category.” If you also create a custom taxonomy and have a “cured ham” entry in it, there will be a different row in the wp_term_taxonomy table for that mapping, along with its unique key. 3. Four rows in the wp_term_relationships table map the “cured ham in category” identifi er to the post identifi ers for each of the posts that are in the category.

    The workhorse operator in working with taxonomy tables is the SQL JOIN, sometimes referred to as the “product” of two (or more) tables. A JOIN builds a temporary table with each row in one

    c06.indd 105 12/6/12 1:29 AM 106 ❘ CHAPTER 6 DATA MANAGEMENT

    table mapped to every row in the second and successive tables; then the WHERE part of a JOIN opera- tion selects those rows where specifi c fi elds in each row match. To fi nd all of the posts in the “cured ham” category, WordPress fi rst fi nds the identifi er for this term and taxonomy pair, selects the appropriate rows from the wp_term_relationships table, and then does a JOIN on the wp_posts and the selected rows from the relationships table: that last JOIN is SQL-ese for “extract all of the posts with identifi ers in this list” where the list is computed on-the-fl y.

    Figure 6-2 shows a graphical representation of the joins between the wp_posts table and taxonomy tables in WordPress.

    FIGURE 6-2: Taxonomy tables relationship

    While this makes the SQL for selecting content associated with a particular tag or category more complex, requiring the use of a multi-table JOIN operations to implement the “name in a taxonomy in a relationship” matching, it is powerful in allowing content to be given rich and multi-valued descriptions, and for category, taxonomy, and tag names to have independent name spaces.

    WORDPRESS DATABASE CLASS

    WordPress features an object class with method functions for working with the database directly. This database class is called wpdb and is located in wp-includes/wp-db.php. Any time you are querying the WordPress database in PHP code, you should use the wpdb class. The main reason for using this class is to allow WordPress to execute your queries in the safest way possible.

    Simple Database Queries When using the wpdb class, you must fi rst defi ne $wpdb as a global variable before it will be available for use. To do so, just drop this line of code directly preceding any $wpdb function call:

    global $wpdb;

    One of the most important functions in the wpdb class is the prepare() function. This function is used for escaping variables passed to your SQL queries. This is a critical step in preventing SQL injection attacks on your website. All queries should be passed through the prepare function before being executed. Here’s an example:

    c06.indd 106 12/6/12 1:29 AM WordPress Database Class ❘ 107

    query( $wpdb->prepare( "INSERT INTO $wpdb->my_custom_table ( id, field_key, field_value ) VALUES ( %d, %s, %s )", 1, $field_key, $field_value ) );

    ?>

    This example adds data into a non-default, custom table in WordPress that you would have previ- ously created. When using prepare(), make sure to replace any variables in your query with %s for strings and %d for integers. Then list the variables as parameters for the prepare() function in the exact same order. In the preceding example, %d represents 1, %s represents $field_key, and the second %s represents $field_value. The prepare function is used on all queries from here on out.

    Notice that this example uses $wpdb->my_custom_table to reference the table in WordPress. This translates to wp_my_custom_table if wp_ is the table prefi x. This is the proper way to determine the correct table prefi x when working with tables in the WordPress database.

    NOTE When installing WordPress, you can set a custom database table prefi x. By default, this is wp_, but many people choose to change this prefi x for security purposes. Using $wpdb-> is the correct way to determine what this table prefi x is for any WordPress installation.

    The wpdb query() method is used to execute a simple query. This function is primarily used for SELECT and DELETE statements. Despite its name, it’s not only for SQL SELECT queries, but will execute any SQL statement against the database. Here’s a basic query function example:

    query( $wpdb->prepare( " DELETE FROM $wpdb->my_custom_table WHERE id = '1' AND field_key = 'address' " ) );

    ?>

    As you can see, you execute your query using the wpdb class query() function to delete the field "address" with an ID of 1. Although the query() function allows you to execute any SQL query on the WordPress database, other database object class functions are more appropriate for SELECT queries. For instance, the get_var() function is used for retrieving a single variable from the database:

    get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->comments;" ) ); echo '

    Total comments: ' . $comment_count . '

    '; ?>

    c06.indd 107 12/6/12 1:29 AM 108 ❘ CHAPTER 6 DATA MANAGEMENT

    This example retrieves a count of all comments in WordPress and displays the total number. Although only one scalar variable is returned, the entire result set of the query is cached. It’s best to try and limit the result set returned from your queries using a WHERE clause to only retrieve the records you actually need. In this example, all comment record rows are returned, even though you display the total count of comments. This would obviously be a big memory hit on larger websites.

    Complex Database Operations To retrieve an entire table row, you’ll want to use the get_row() function. The get_row() function can return the row data as an object, an associative array, or a numerically indexed array. By default, the row is returned as an object, in this case an instance of the per-post data. Here’s an example:

    get_row( $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE ID = 1" ) ); echo $thepost->post_title; ?>

    This retrieves the entire row data for post ID 1 and displays the post title. The properties of $the- post object are the column names from the table you queried, which is wp_posts in this case. To retrieve the results as an array, you can send in an additional parameter to the get_row() function:

    get_row( $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE ID = 1" ), ARRAY_A ); print_r ( $thepost ); ?>

    By using the ARRAY_A parameter in get_row(), your post data is returned as an associative array. Alternatively, you could use the ARRAY_N parameter to return your post data in a numerically indexed array.

    Standard SELECT queries should use the get_results() function for retrieving multiple rows of data from the database. The following function returns the SQL result data as an array:

    get_results( $wpdb->prepare( "SELECT ID, post_title FROM $wpdb->posts WHERE post_status = 'publish' " ) );

    foreach ( $liveposts as $livepost ) { echo '

    ' .$livepost->post_title. '

    '; } ?>

    The preceding example is querying all published posts in WordPress and displaying the post titles. The query results are returned and stored as an array in $liveposts, which you can then loop through to display your query values.

    The WordPress database class also features specifi c functions for UPDATE and INSERT statements. These two functions eliminate the need for custom SQL queries because WordPress will create

    c06.indd 108 12/6/12 1:29 AM WordPress Database Class ❘ 109

    them for you based on the values passed into the function. Here is how the insert() function is structured:

    $wpdb->insert( $table, $data );

    The $table variable is the name of the table you want to insert a value into. The $data variable is an array of fi eld names and data to be inserted into those fi eld names. So, for example, if you want to insert data into a custom table, you would execute this:

    insert( $wpdb->my_custom_table, array( 'field_one' => $newvalueone, 'field_two' => $newvaluetwo ) );

    ?>

    The fi rst thing you do is set two variables to store the data you want to insert. Next, you execute the insert() function, passing in both variables through an array. Notice how you set field_one and field_two as the two fi elds you are inserting. You can pass any fi eld available in the table you are inserting with data to insert into that fi eld.

    The update() function works very similarly to the insert() function, except you also need to set the WHERE clause variable so WordPress knows which records to update:

    $wpdb->update( $table, $data, $where );

    The $where variable is an array of fi eld names and data for the SQL WHERE clause. This is nor- mally set to the unique ID of the fi eld you are updating, but can also contain other fi eld names from the table.

    update( $wpdb->posts, array( 'post_title' => $newtitle, 'post_content' => $newcontent ), array( 'ID' => $my_id ) ); ?>

    First you set your updated title and content variables. You also set the variable $my_id, which con- tains the ID of the post you want to update. Next, you execute the update() function. Notice that the third parameter you send is an array containing your WHERE clause values, in this case the post ID. The preceding query updates the title and content for post ID 1. Remember that you can send multiple values through the WHERE parameter when updating a table record.

    The insert() and update() functions shown do not need to be wrapped with the prepare() function. Both of these functions actually use the prepare() function after concatenating the query from the values passed to the functions. This is a much easier method than manually creating your INSERT and UPDATE queries in WordPress.

    c06.indd 109 12/6/12 1:29 AM 110 ❘ CHAPTER 6 DATA MANAGEMENT

    Dealing with Errors Any time you are working with queries, it’s nice to see error messages. By default, if a custom query fails, nothing is returned so it’s hard to determine what is wrong with your query. The wpdb class provides functions for displaying MySQL errors to the page. Here’s an example of using these functions:

    show_errors(); $liveposts = $wpdb->get_results( $wpdb->prepare("SELECT ID, post_title FROM $wpdb->posts_FAKE WHERE post_status = 'publish'") ); $wpdb->print_error(); ?>

    The show_errors() function must be called directly before you execute a query. The print_ error() function must be called directly after you execute a query. If there are any errors in your SQL statement, the error messages are displayed. You can also call the $wpdb->hide_errors() function to hide all MySQL errors, or call the $wpdb->flush() function to delete the cached query results. The database class contains additional variables that store information about WordPress queries. Following is a list of some of the more common variables:

    var_dump( $wpdb->num_queries ); // total number of queries ran var_dump( $wpdb->num_rows ); // total number of rows returned by the last query var_dump( $wpdb->last_result ); // most recent query results var_dump( $wpdb->last_query ); // most recent query executed var_dump( $wpdb->col_info ); // column information for the most recent query

    Add the preceding code directly after you execute a query to see the results. This is very useful when determining why a database query isn’t working as expected.

    Another very powerful database variable is the $queries variable. This stores all of the queries run by WordPress. To enable this variable, you must fi rst set the constant valueSAVEQUERIES to true in your wp-config.php fi le. This tells WordPress to store all of the queries executed on each page load in the $queries variable. First drop this line of code in your wp-config.php fi le:

    define( 'SAVEQUERIES', true );

    Now all queries will be stored in the $queries variable. You can display all of the query informa- tion like so:

    var_dump( $wpdb->queries ); // displays all queries executed during page load

    This is especially handy when troubleshooting slow load times. If a plugin is executing an obscene number of queries, that can dramatically slow down load times in WordPress. Remember to disable the SAVEQUERIES constant option when you are fi nished viewing queries because storing all queries can also slow down load times.

    c06.indd 110 12/6/12 1:29 AM Direct Database Manipulation ❘ 111

    The database query class is a major asset when working with the WordPress database directly, as you will see when developing a plugin or building a more complex Loop. All of the previously mentioned database class functions use specifi c escaping techniques to verify that your queries are executed in the safest manner possible. To borrow from Randall Munroe’s “Little Bobby Tables” xkcd joke (xkcd #327), you don’t want a user handcrafting an input item that contains DROP TABLES as a malicious SQL injection, resulting in the loss of your WordPress database tables. The query preparation and escaping functions ensure that inputs don’t become SQL functions, no matter how craftily they’re set up. It is essential that you follow these methods for querying data to ensure your website is the most effi cient and uses the safest techniques possible.

    DIRECT DATABASE MANIPULATION

    There may be times when you want to work with the WordPress database data directly. This can include accessing custom database tables created by a plugin or theme. To do this, you’ll need to use SQL to query the data from the MySQL database. Remember that the WordPress APIs provide access to all of the WordPress tables and only very occasionally will you need to access the tables directly. All example queries in this chapter use the wp_ prefi x for tables, but your database tables may use a different prefi x as defi ned in your wp-config.php fi le when installing WordPress. One of the most common methods for working with a WordPress database directly is by using php- MyAdmin, shown in Figure 6-3. As described in Chapter 3, phpMyAdmin is a free software tool provided by most hosting companies for administering MySQL databases through a web interface. Most of the examples in this section involve direct interaction with MySQL, and you’ll need to use an SQL command line for their execution. Figure 6-3 shows the default database view using phpMyAdmin.

    FIGURE 6-3: phpMyAdmin viewing a WordPress database

    To run SQL statements in phpMyAdmin simply click the SQL tab across the top. Here you can execute any queries against your WordPress database. We always recommend creating your query directly in phpMyAdmin fi rst before moving it over to your PHP scripts. The reasoning behind this is that debugging SQL statements is much faster directly in phpMyAdmin than it is using PHP code in WordPress. Once you have perfected your query, you can use it in your PHP code and you can

    c06.indd 111 12/6/12 1:29 AM 112 ❘ CHAPTER 6 DATA MANAGEMENT

    be confi dent the results will be as expected. In the examples that follow you’ll be using raw SQL queries. Remember that if you want to run these queries in a theme or plugin, you’ll need to wrap the queries in the WordPress database class.

    One of the most commonly accessed tables is the wp_posts table. Remember that this table stores all posts, pages, custom post types, revisions, and even attachment records. The different types of content are defi ned by the post_type fi eld. WordPress 2.9 introduced the ability for developers to defi ne custom post types, which is discussed in greater detail in Chapter 7. This means that addi- tional post_type values may exist in this fi eld. To view all post revisions in your database, you can run this query:

    SELECT * FROM wp_posts WHERE post_type = 'revision'

    This returns all records in wp_posts that are of a revision post_type. You can modify the preced- ing query to view all post attachments that have been uploaded to WordPress:

    SELECT guid, wp_posts.* FROM wp_posts WHERE post_type = 'attachment'

    This example places the fi eld guid as the fi rst value to be returned in the query. The guid fi eld con- tains the full URL of the attachment fi le on the server.

    The wp_options table contains all of the settings saved for your WordPress installation. Options saved in this table are saved with an option_name and option_value. Therefore, the actual fi eld name you call will always be those two names, rather than a specifi c fi eld based on the option value. Following are two extremely important records in this table:

    SELECT * FROM wp_options WHERE option_name IN ( 'siteurl','home' )

    This query returns two records, one where option_name is home and another where option_name is siteurl. These are the two settings that tell WordPress what the domain of your website is. If you ever need to change your website’s domain, you can run a query to update these two values like so:

    UPDATE wp_options SET option_value = 'http://yournewdomain.com' WHERE option_name IN ('siteurl','home')

    Once this query runs, your website will instantly run under the new domain. Remember that this only updates the website’s domain in WordPress. Attachment URLs in posts and pages will also need to be updated to point to the new domain. Plugins can also store the domain information, so be sure to test in a development environment before updating a production website. If you access the old domain, you will be redirected to the new one. If you were logged in, your cookies and ses- sion will be invalidated and you will have to log in again. This is a great technique if you built a new website under a subdomain (for example, http://new.example.com) and are updating the URLs to push the website live.

    c06.indd 112 12/6/12 1:29 AM Direct Database Manipulation ❘ 113

    The wp_options table contains other very important fi elds. To view all active plugins on your web- site, you can view the active_plugins option_name like so:

    SELECT * FROM wp_options WHERE option_name = 'active_plugins'

    The options table also stores all options defi ned by plugins. Most plugins activated in WordPress will have some type of settings page. These settings are generally saved in wp_options so the plugins can retrieve these settings as needed. For example, the Akismet plugin stores an option named akismet_spam_count that stores the total number of spam comments. You can view this option by running the following query:

    SELECT * FROM wp_options WHERE option_name = 'akismet_spam_count'

    The wp_users table contains all of the users you currently have set up in WordPress. If you allow open registration on your website, new users will be created in this table as they join your site. The wp_users table stores very important user information including username, password, e-mail, website URL, and date registered. Say you want to export all of your users’ e-mail addresses. You can easily do so by running the following query:

    SELECT DISTINCT user_email FROM wp_users

    Now you can easily export all of the e-mail addresses loaded into WordPress! Another common query used in wp_users is to reset a user’s password. You can do this in a couple of different ways, but if you are absolutely locked out of WordPress, you can always reset the password directly in the database. To do so, you need to update the user_pass fi eld from the MySQL command line:

    UPDATE wp_users SET user_pass = MD5('Hall0w33n') WHERE user_login ='admin' LIMIT 1;

    Running this query resets the admin password to Hall0w33n. Notice how you wrap the new password in MD5(). This converts the password to an MD5 hash. Since WordPress 2.5, passwords are now salted and hashed using the phpass library rather than MD5. Not to worry, however, because WordPress is built to detect MD5 hash passwords and convert them to phpass encryption instead. So the preceding query will successfully reset your password in WordPress.

    The wp_comments table stores all comments submitted to your website. This table contains the com- ment, author, e-mail, website URL, IP address, and more. Here’s an example query for displaying comments:

    SELECT wc.* FROM wp_posts wp INNER JOIN wp_comments wc ON wp.ID = wc.comment_post_ID WHERE wp.ID = '1554'

    c06.indd 113 12/6/12 1:29 AM 114 ❘ CHAPTER 6 DATA MANAGEMENT

    This query returns all comments for post ID 1554. Another important fi eld in wp_comments is the user_id fi eld. If a user is logged in to your website and posts a comment, this fi eld will contain his or her user ID. Consider the following code, which displays all comments left by the user admin:

    SELECT wc.* FROM wp_comments wc INNER JOIN wp_users wu ON wc.user_id = wu.ID WHERE wu.user_login = 'admin'

    In the database diagram in Figure 6-1, the arrows show the relationships between each table. This is incredibly useful when writing custom queries to retrieve data directly from the database. For example, to retrieve all comments for a particular post you could run this query:

    SELECT * FROM wp_comments INNER JOIN wp_posts ON wp_comments.comment_post_id = wp_posts.ID WHERE wp_posts.ID = '1'

    This query returns all comments for post ID 1. Notice how you join the wp_comments.comment_ post_ID fi eld to the wp_posts.ID fi eld. The SQL JOIN is necessary because there is an N:1 relationship between comments and posts; each post may have many comments but comments apply to only one post. These two fi elds are shown in the diagram as the joining fi elds for these tables. Also consider the following example, which demonstrates how to join the wp_users and wp_usermeta tables together:

    SELECT * FROM wp_users INNER JOIN wp_usermeta ON wp_users.ID = wp_usermeta.user_id WHERE wp_users.ID = '1'

    As you can see in the database diagram, the wp_users.ID fi eld was joined to the wp_usermeta .user_id fi eld. The preceding query retrieves all of the user information, including user metadata, for user ID 1, which is the default admin account. Again, the database diagram makes it extremely easy to determine how tables are joined by index value inside the WordPress database, and how logical INNER JOIN operations can build result sets of related table rows. If you are interested in learning more about SQL, you can read some amazing tutorials at http://www.w3schools.com/sql/default.asp.

    SUMMARY

    This chapter covered the WordPress database schema, database table relationships, the WordPress database class, and the proper way to debug database queries. Whether working with themes, plugins, or custom functions, understanding how to work with the WordPress database is very important. Understanding where and how WordPress stores data in the database can help as you develop more complex website features. Next you’ll cover custom content in WordPress using custom post types. You’ll also cover custom taxonomies, custom metadata, and the power and importance of both when developing WordPress websites.

    c06.indd 114 12/6/12 1:29 AM 7 Custom Post Types, Custom Taxonomies, and Metadata

    WHAT’S IN THIS CHAPTER?

    ➤ Understanding and creating custom post types ➤ Displaying and using custom post type content ➤ Creating and using custom taxonomies ➤ Understanding and using metadata

    The most important part of any WordPress website is the content. WordPress, by default, has various types of content and taxonomies defi ned, but often, you will need to defi ne your own types of content to build the exact website you want. Over the past few years, WordPress has introduced some very advanced, and easy-to-use, tools for working with all sorts of custom content. This has helped WordPress evolve into a full- fl edged content management system capable of powering absolutely any type of website setup, regardless of the content. In this chapter, you learn how to create custom post types and content in WordPress. You also learn how to work with custom taxonomies to group and classify your content. Finally, you learn how to attach and retrieve arbitrary pieces of metadata to your content.

    UNDERSTANDING DATA IN WORDPRESS

    When working with various types of data in WordPress, it’s important to understand what that data is and how it can be customized. WordPress has fi ve predefi ned post types in a default installation: 1. Post — Blog posts or articles generally ordered by date 2. Page — Hierarchical static pages of content

    c07.indd 115 12/6/12 1:31 AM 116 ❘ CHAPTER 7 CUSTOM POST TYPES, CUSTOM TAXONOMIES, AND METADATA

    3. Attachment — Media uploaded to WordPress and attached to post type entries, such as images and fi les 4. Revision — A revision of a post type used as backup and can be restored if needed 5. Nav Menus — Menu items added to a nav menu using WordPress’ menu management feature For a basic blog or smaller website, these default post types are all you might need. However, if you plan on building a more complex CMS-type website, you’ll want to utilize the power of custom post types.

    What Is a Custom Post Type? A custom post type in WordPress is a custom defi ned piece of content. Using custom post types, you can defi ne any type of content in WordPress, and you are no longer forced to use just the default post types listed in the previous section. This opens to door to an endless number of possibilities. Potential custom post type ideas include, but are not necessarily limited to, the following: ➤ Products ➤ Events ➤ Videos ➤ Rotator ➤ Testimonials ➤ Quotes ➤ Error Log Remember that custom post types can be absolutely anything, not just public-facing pieces of content. For example, you can set up a custom post type as an error log to track errors in your application. When it comes to custom post types, the only limitation is your imagination.

    Register Custom Post Types To create a new custom post type, you’ll use the register_post_type() function as shown here: The register_post_type() function accepts two parameters: 1. $post_type — The name of the post type. Should contain only lowercase letters, no spaces, and a max length of 20 characters. 2. $args — An array of arguments that defi ne the post type and various options in WordPress. Now look at a basic example of registering a custom post type. You can register a post type in WordPress in two different places. The fi rst is in your theme’s functions.php fi le. The second is in a custom plugin. You could add the following code to a custom plugin, but for this example, add the following code to your theme’s functions.php fi le.

    c07.indd 116 12/6/12 1:31 AM Understanding Data in WordPress ❘ 117

    function prowp_register_my_post_types() {

    register_post_type( 'products', array( 'labels' => array( 'name' => 'Products' ), 'public' => true, ) );

    } ?>

    Now visit your WordPress admin dashboard. You’ll notice that a new menu called Products has appeared just below Comments, as shown in Figure 7-1. That is the new custom post type you just registered with the preceding code. As you can see, WordPress will automatically create the admin UI for your new custom post type. The new menu item allows you to create new post type FIGURE 7-1: Products product entries as well as edit existing entries, just like posts and pages in custom post type WordPress. This is a basic example, but you can already tell the ease with which you can defi ne custom content in WordPress.

    NOTE You should always use the init action hook when registering your custom post types. This is the fi rst hook available after WordPress is fully initialized and will verify that your custom post type is registered early enough in the process.

    There are many different arguments available when registering your custom post type. It’s important to understand these arguments to know what’s available.

    public Sets whether a post type is publicly available on the admin dashboard or front-end of your website. By default, this is set to false, which will hide the post type from view. The default settings for show_ui, exclude_from_search, publicly_queryable, and show_in_nav_menus are inherited from this setting.

    show_ui This determines whether or not to create a default UI in the WordPress admin dashboard for managing this post type. It defaults to the value defi ned by the public argument.

    publicly_queryable This determines if the post type content can be publicly queried on the front end of your website. If it is set to false, all front end queries for entries under the custom post type will return a 404, since it is not allowed to be queried. It defaults to the value defi ned by the public argument.

    c07.indd 117 12/6/12 1:31 AM 118 ❘ CHAPTER 7 CUSTOM POST TYPES, CUSTOM TAXONOMIES, AND METADATA

    exclude_from_search This allows you to exclude custom post type entries from the WordPress search results. It defaults to the value defi ned by the public argument.

    show_in_nav_menus This determines if the post type is available for selection in the menu management feature of WordPress. It defaults to the value defi ned by the public argument.

    supports This argument allows you to defi ne what meta boxes appear on the screen when creating or editing a new post type entry. This defaults to the title and editor. Several options are available:

    ➤ title — Sets the post title. ➤ editor — Displays the content editor on the post editing screen with a media uploader. ➤ author — Selects box to choose the author of the post. ➤ thumbnail — Featured image meta box for the post. ➤ excerpt — Displays an excerpt editor on the post type editing screen. ➤ comments — Sets whether comments will be enabled for posts of this type. ➤ trackbacks — Sets whether trackbacks and pingbacks will be enabled for posts of this type. ➤ custom-fields — Displays the custom fi eld editing area meta box. ➤ page-attributes — Displays the attributes box for choosing the post order. The hierarchical argument must be set to true for this to work. ➤ revisions — Displays the post revisions meta box. ➤ post-formats — Displays the post formats meta box with registered post formats.

    labels This sets an array of labels that represents your post type in the admin dashboard. See the section, “Setting Post Type Labels,” later in this chapter for details on each label.

    hierarchical The hierarchical argument allows you to defi ne if the post type is hierarchical, like pages in WordPress. A hierarchical post type allows you to have a tree-like structure for your post type content. By default, this argument is set to false.

    has_archive This enables your post type to have an archive page. A post type archive page is like the WordPress posts page, which displays the site’s latest blog entries. This allows you to display a list of your post type entries, with the order being defi ned in your theme’s template file.

    c07.indd 118 12/6/12 1:31 AM Understanding Data in WordPress ❘ 119

    can_export This determines if the post type content is available for export using the built-in WordPress export feature under Tools ➪ Export. This argument is set to true by default.

    taxonomies This names an array of registered taxonomies to attach to the custom post type. For example, you can pass in category and post_tag to attach the default Categories and Tags taxonomies to your post type. By default, there are no taxonomies attached to a custom post type.

    menu_position This enables you to set the position in which the custom post type menu shows in the admin menu. By default, new post types are displayed after the Comments menu.

    menu_icon This sets a custom menu icon for your post type. By default, the posts icon is used.

    show_in_menu This determines whether or not to display the admin menu for your post type. This argument accepts three values: true, false, or a string. The string can be either a top-level page, such as tools.php, or edit.php?post_type=page. You can also set the string to the menu_slug parameter to add the custom post type as a submenu item to an existing custom menu. It defaults to the value defi ned by the show_ui argument.

    show_in_admin_bar This sets whether or not to show your custom post type in the WordPress admin bar. It defaults to the value defi ned by the show_in_menu argument.

    capability_type This names a string or an array of the capabilities for this post type. By default, the value is set to post.

    capabilities This is an array of custom capabilities required for editing, deleting, viewing, and publishing posts of this post type.

    query_var This argument sets the query variable for posts of this post type. The default value is true and is set to the $post_type value.

    c07.indd 119 12/6/12 1:31 AM 120 ❘ CHAPTER 7 CUSTOM POST TYPES, CUSTOM TAXONOMIES, AND METADATA

    rewrite The rewrite argument creates the unique permalinks for this post type. This allows you to customize the post type slug in your URL. This argument can be set to true, false, or an array of values. If passing an array, it accepts the following values:

    ➤ slug — Sets a custom permalink slug. Defaults to the $post_type value. ➤ with_front — Sets whether your post type should use the front base from your permalink settings. For example, if you prefi xed your permalinks with /blog, and with_front is set to true, your post type permalinks would include /blog at the beginning. ➤ pages — Sets whether the permalink provides for pagination. Defaults to true. ➤ feeds — Sets whether a feed permalink will be built for this post type. Defaults to has_archive value. By default this argument is set to true and the $post_type is used as the slug. This section has covered a lot of custom post type arguments. The following example puts some of the more common arguments to use.

    function prowp_register_my_post_types() {

    $args = array( 'public' => true, 'has_archive' => true, 'taxonomies' => array( 'category' ), 'rewrite' => array( 'slug' => 'product' ), 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'comments' ) );

    register_post_type( 'products', $args );

    } ?>

    In this example, you fi rst set the post type to be public. You also enabled the post type to have an archive page by setting the has_archive argument to true. Using the taxonomies argument, you attached the default Category taxonomy to your product’s custom post type.

    In this example, you want to change the permalink slug for your post type. Instead of http:// example.com/products/zombie-bait, using the default slug products from the post type name, you want to set your post type slug to the singular product. This will generate your permalink as http://example.com/product/zombie-bait. This is done using the rewrite argument and defi ning a custom slug for your post type. The fi nal argument you set is supports. The code adds the title, editor, author, featured image, and comments meta box to your custom post type create and edit screens.

    c07.indd 120 12/6/12 1:31 AM Understanding Data in WordPress ❘ 121

    NOTE When registering a new custom post type, it’s important to fl ush the rewrite rules in WordPress. You can do this by calling the function flush_ rewrite_rules() in your plugin’s activation hook or manually by going to Settings ➪ Permalinks and saving your permalink settings. This will eliminate 404 errors on your new post type permalinks.

    To learn more about the register_post_type() function, visit the offi cial Codex page at http:// codex.wordpress.org/Function_Reference/register_post_type.

    Setting Post Type Labels When creating a custom post type in WordPress, several text strings are shown throughout the WordPress admin dashboard for your post type. These text strings are typically a link, button, or extra information about the post type. By default, the term “post” is used for non-hierarchical post types and “page” for hierarchical post types. For example, when you use the basic custom post type registration code earlier in this chapter, you’ll notice the text “Add New Post” at the top of the page when you add a new Product. The reason for this is Product is a post of type Product. This isn’t very accurate, as you aren’t actually adding a post, but rather a new Product. Setting the labels argument when registering your custom post type will allow you to defi ne exactly what is shown. The available labels for your custom post types include:

    ➤ name — General name for the post type, which is usually plural. Used in the WordPress admin and by other plugins and themes. ➤ singular_name — The singular version of the name for the post type. It is also used in the WordPress admin and by other plugins and themes. ➤ add_new — The label for the Add New submenu item. The text defaults to “Add New.” ➤ add_new_item — Used as the header text on the main post listing page to add a new post. By default, the text is “Add New Post/Page.” ➤ edit_item — Used as the text for editing an individual post. Defaults to “Edit Post/Page.” ➤ new_item — Text for creating a new post. By default, it is set to “New Post/Page.” ➤ view_item — The text for viewing an single post entry. Defaults to “View Post/Page.” ➤ search_items — Text displayed for searching the posts of this type. It defaults to “Search Posts/Pages.” ➤ not_found — The text shown when no posts were found in a search. By default, it displays “No posts/pages found.” ➤ not_found_in_trash — The text shown when no posts are in the trash. Defaults to “No posts/pages found in Trash.” ➤ parent_item_colon — Text shown when displaying a post’s parent. This text is only used with hierarchical post types and displays “Parent Page:” by default.

    c07.indd 121 12/6/12 1:31 AM 122 ❘ CHAPTER 7 CUSTOM POST TYPES, CUSTOM TAXONOMIES, AND METADATA

    Setting each value makes for a much better user experience when administering a WordPress website. In the following code, you’ve modifi ed your original custom post type registration code and set the labels for the Product post type:

    function prowp_register_my_post_types() {

    $labels = array( 'name' => 'Products', 'singular_name' => 'Product', 'add_new' => 'Add New Product', 'add_new_item' => 'Add New Product', 'edit_item' => 'Edit Product', 'new_item' => 'New Product', 'all_items' => 'All Products', 'view_item' => 'View Product', 'search_items' => 'Search Products', 'not_found' => 'No products found', 'not_found_in_trash' => 'No products found in Trash', 'parent_item_colon' => '', 'menu_name' => 'Products' );

    $args = array( 'labels' => $labels, 'public' => true );

    register_post_type( 'products', $args );

    } ?>

    Working with Custom Post Types Now that you understand how to register a custom post type, let’s explore how you use them in your WordPress website. Typically it’s the job of your theme to display posts on the front end of your site. However, that may not always be the case as certain custom post types may not need to be publicly displayed — for example, an error log. It all depends on what the function of your post type is.

    To display custom post type data, you can use the WP_Query custom Loop example from Chapter 5. Remember that WP_Query accepts a post_type parameter that determines what type of content to return. In the example that follows, you’ll return all of your product entries in WordPress:

    $args = array( 'posts_per_page' => '-1', 'post_type' => 'products',

    c07.indd 122 12/6/12 1:31 AM Understanding Data in WordPress ❘ 123

    );

    $myProducts = new WP_Query( $args );

    // The Loop while ( $myProducts->have_posts() ) : $myProducts->the_post(); ?>

    // Reset Post Data wp_reset_postdata();

    Notice the post_type parameter is set to products, which is the $post_type parameter value used when you registered the Products custom post type. Now modify the custom Loop to return only products in the Specials category:

    $args = array( 'posts_per_page' => '-1', 'post_type' => 'products', 'tax_query' => array( array( 'taxonomy' => 'category', 'field' => 'slug', 'terms' => 'specials' ) ) );

    $myProducts = new WP_Query( $args );

    Using the tax_query parameter in WP_Query, the custom Loop will return only product post type entries assigned to the Specials category.

    You can use all of the same methods for creating custom Loops with WP_Query, as covered in detail in Chapter 5, to display your custom post type content. It’s easy to see the power custom post types bring to WordPress when developing more complex websites.

    Custom Post Type Template Files Earlier in the chapter you learned about the has_archive argument when registering a custom post type. Enabling this argument will allow you to create an archive template fi le that will display all of your custom post type entries by default. The archive template for a custom post type must be named in the form of archive-{post-type}.php. For example, an archive template for your Products custom post type would be named archive-products.php. This archive template is a perfect place to display all of your products. Just like the archive template, WordPress will also recognize a single template for your post type entries. This is the template that is loaded when you visit a single entry for your custom post type.

    c07.indd 123 12/6/12 1:31 AM 124 ❘ CHAPTER 7 CUSTOM POST TYPES, CUSTOM TAXONOMIES, AND METADATA

    The single template must be named in the form of single-{posttype}.php. So your products single template would be named single-products.php. When visiting a single product URL, such as http://example.com/products/zombie-bait, the single-products.php template would load. Theme template fi les, including custom post types, are covered in more detail in Chapter 9.

    Special Post Type Functions WordPress features many different post type–specifi c functions to make working with custom post types that much easier. In this section, you will review some of the more common functions you might use when building your websites.

    To return a list of all registered post types in WordPress, you’ll use the get_post_types() function.

    This function accepts three optional parameters: 1. $args — An array of arguments to match against the post type. 2. $output — The type of output to return, either names or objects. Defaults to names. 3. $operator — Operator to use with multiple $args. Defaults to and. Using the get_post_types() function, use the following to return a list of all custom post types registered in WordPress:

    $args = array( 'public' => true, '_builtin' => false );

    $post_types = get_post_types( $args, 'names', 'and' );

    foreach ( $post_types as $post_type ) { echo '

    '. $post_type. '

    '; }

    As shown in the preceding code, you’ll set two arguments in the $args array: public and _ builtin. The public argument will only return custom post types that are set to be publicly viewable. The _builtin argument is set to false, which will not return default post types like posts and pages. You also set the $output argument to return just the post type name, and the $operator argument to use “and” for the multiple $args you passed to the function.

    To determine what post type a piece of content is, you’ll use the get_post_type() function:

    This function accepts only one parameter — $post — which is a post object or a post ID.

    c07.indd 124 12/6/12 1:31 AM Understanding Data in WordPress ❘ 125

    You can display the post type of a post using the following code:

    ID ); ?>

    There may be a time when you want to work with a custom post type that was created by a plugin or theme. The fi rst thing you should always do is to verify that the custom post type you are looking for exists. To do so, you’ll use the post_type_exists() function.

    The function accepts a single required parameter — $post_type — which is the post type you want to verify has been registered.

    If you wanted to verify the products custom post type exists, you use this code:

    if( post_type_exists( 'products' ) ) { echo 'The Products post type exists'; }

    Another useful function when working with other custom post types is add_post_type_support(). This function allows you to register support for certain features on a post type, such as the featured image meta box.

    This is a useful function if the existing post type doesn’t have support for a feature that you need. The add_post_type_support() function accepts two parameters: 1. $post_type — The post type name you are adding support to 2. $supports — A string or array of features to add As an example, assume the products post type does not support featured images or comments. To add support for both of these features, use the following code example:

    add_post_type_support( 'products', array( 'thumbnail', 'comments' ) );

    This function is very useful if you need to work with a custom post type that is defi ned in a separate plugin or theme. Rather than hacking the registration code in that plugin or theme, you can use the add_post_type_support() function to enable any feature needed for your code to work. WordPress also features a function to change the post type of a post entry. You can do so by using the set_post_type() function.

    The function accepts two parameters: 1. $post_id — The ID of the post you want to update. This fi eld is required. 2. $post_type — The post type name to change the post to. This is an optional fi eld and defaults to post.

    c07.indd 125 12/6/12 1:31 AM 126 ❘ CHAPTER 7 CUSTOM POST TYPES, CUSTOM TAXONOMIES, AND METADATA

    WORDPRESS TAXONOMY

    Taxonomy is defi ned as a way to group similar items together. This basically adds a relational dimension to your website’s content. In the case of WordPress, you use categories and tags to group your posts. By grouping these posts, you are defi ning the taxonomy of those posts. Taxonomy can be hierarchical (that is, categories and subcategories), but it is not required as with the case of tags.

    Default Taxonomies By default, WordPress comes loaded with three taxonomies: 1. Category — A bucket for grouping similar posts together 2. Tag — A label attached to a post 3. Link category — A bucket for grouping similar links together Categories are hierarchical and defi ned when creating a post. Tags do not use hierarchy and are also defi ned when creating a post. Link categories are used when grouping similar links together using the WordPress link manager. All three out-of-the-box taxonomies are available for use in a default installation of WordPress. Each category or tag you create is a term of that taxonomy. For example, a category named Music is a term of the category taxonomy. A tag named Ketchup is a term of the tag taxonomy. Understanding taxonomy and terms will help you when defi ning your own custom taxonomies in WordPress. Understanding how you can classify your content using a solid taxonomy structure will make structuring website content in WordPress much easier from the start. Developing a solid taxonomy framework enables easy and accurate information access throughout your website.

    Taxonomy Table Structure WordPress features three database tables that store all taxonomy information: wp_terms, wp_term_ relationships, and wp_term_taxonomy. This taxonomy schema, which was added in WordPress 2.3, makes the taxonomy functionality extremely fl exible in WordPress. This means you can create and defi ne any type of custom taxonomy to use on your website.

    The wp_terms table stores all of your taxonomy terms. This can be categories, tags, link categories, and any custom taxonomy terms you have defi ned. The wp_term_taxonomy table defi nes what taxonomy each term belongs to. For example, all of your tag IDs will be listed in this table with a taxonomy value of post_tag. If you created a custom taxonomy, the taxonomy value would be the name of your custom taxonomy. The wp_term_relationships table is the cross-reference table that joins taxonomy terms with your content. For example, when you assign a tag to your post, a new record is created here joining your post ID and the term ID together.

    c07.indd 126 12/6/12 1:31 AM WordPress Taxonomy ❘ 127

    Understanding Taxonomy Relationships To really understand the relationship between the taxonomy tables, it’s helpful to look at a database diagram of the taxonomy table structure, as shown in Figure 7-2.

    wp_term_relationships object_id BIGINT(20) term_taxonomy_id BIGINT(20) term_order INT(11) Indexes PRIMARY term_taxonomy_id

    wp_term_taxonomy term_taxonomy_id BIGINT(20) term_id BIGINT(20) taxonomy VARCHAR(32) description LONGTEXT wp_term parent BIGINT(20) count BIGINT(20) term_id BIGINT(20) Indexes name VARCHAR(200) slug VARCHAR(200) PRIMARY term_group BIGINT(10) term_id_taxonomy Indexes taxonomy PRIMARY slug name

    FIGURE 7-2: WordPress taxonomy table structure

    As you can see, the three taxonomy tables are joined together by unique IDs. The following is a query to display all posts along with all taxonomy terms assigned to those posts:

    SELECT .name, wp.post_title, wp.post_date FROM wp_terms wt INNER JOIN wp_term_taxonomy wtt ON wt.term_id = wtt.term_id INNER JOIN wp_term_relationships wtr ON wtt. term_taxonomy_id = wtr.term_taxonomy_id INNER JOIN wp_posts wp ON wtr.object_id = wp.ID WHERE wp.post_type = 'post'

    Notice how you are joining on the table fi elds, as depicted in Figure 7-2. The preceding example returns only three fi elds: the taxonomy term, post title, and post date. This query example returns all posts in your WordPress database along with all taxonomy terms attached to those posts.

    NOTE To learn more about taxonomy table relationships and why WordPress needs to decompose these multi-valued relationships into multiple tables, see the WordPress Taxonomy Tables section of Chapter 6: Data Management.

    c07.indd 127 12/6/12 1:31 AM 128 ❘ CHAPTER 7 CUSTOM POST TYPES, CUSTOM TAXONOMIES, AND METADATA

    BUILDING YOUR OWN TAXONOMIES

    Creating your own custom taxonomies has many benefi ts. Imagine running a food blogging website. When creating new posts, you’ll want to label a certain recipe as Asian, but you also may want to label the individual ingredients, heat factor, prep time, and so on. Building custom taxonomies allows you the freedom to defi ne these different methods of categorizing your content and really expands WordPress from blogging software into a full-fl edged content management system (CMS).

    Custom Taxonomy Overview With the revamp of the taxonomy schema in WordPress 2.3, you now have the capability to defi ne custom taxonomies for your content. WordPress makes it easier than ever to create custom taxonomies, as well as integrate your new taxonomies into WordPress. WordPress 2.8 added the feature to automatically display a meta box to the post type edit screen for adding taxonomy terms directly to your posts. WordPress will also create a menu item to access the new taxonomy admin panel for administering your taxonomy terms.

    Creating Custom Taxonomies Now it’s time to build your fi rst custom taxonomy! You are going to create a simple taxonomy for defi ning Types for your product custom post type registered earlier in this chapter. If you are selling Products online, you’ll need a way to group specifi c Product types together. You are going to set up a custom taxonomy to defi ne each type of Product in WordPress.

    First, you are going to defi ne your new taxonomy using the register_taxonomy() WordPress function. This function allows you to customize how your new taxonomy will work and look. The following code would work in a custom plugin, but for this example, you’ll use the functions.php fi le in your theme folder. Open up functions.php in your theme and add the following code:

    function prowp_define_product_type_taxonomy() {

    register_taxonomy( 'type', 'products', array( 'hierarchical' => true, 'label' => 'Type', 'query_var' => true, 'rewrite' => true ) );

    } ?>

    The taxonomy defi nition starts by calling the init hook, which tells WordPress to execute your prowp_define_product_type_taxonomy() function during initialization. Your function then calls the WordPress function register_taxonomy(). This function is used to create your custom taxonomy based on what values you send.

    c07.indd 128 12/6/12 1:31 AM Building Your Own Taxonomies ❘ 129

    You can now break down the parameters you are sending to the register_taxonomy() function. The fi rst parameter is the taxonomy name, in this case type. This is the name that will defi ne this taxonomy in the database. The second parameter is the object type. For this example, you will use products, which is the name of your custom post type. The third and fi nal parameter is for arguments, meaning you actually send multiple values to this parameter.

    In this example, you’ll pass four arguments. The fi rst is hierarchical, which defi nes whether or not your custom taxonomy can support nested taxonomies, forming a hierarchy. In the preceding example, you set this to true, so your taxonomy will function just like WordPress’ built-in categories that may contain sub-categories. The next argument, label, is used to set the name of your taxonomy for use in admin pages within WordPress. If the query_var argument is set to false, then no queries can be made against the taxonomy; if true then the taxonomy name (with dashes replacing spaces) is used as a query variable in URL strings. Specifying a string value for the query_var overrides the default. For example, query_var => 'strength' would permit URL strings of the form example.com/?strength=weapons to be used to select content from the custom taxonomy.

    The fi nal argument is for rewrite, which you set to true. This tells WordPress whether or not you want a pretty permalink when viewing your custom taxonomy. By setting this to true you can access your custom taxonomy posts such as example.com/type/weapons rather than the ugly method of example.com/?type=weapons.

    Now that you have created your custom taxonomy for type, take a look at FIGURE 7-3: Custom what WordPress has done with your new taxonomy. The fi rst thing you will taxonomy menu notice on your admin dashboard is a new link under the Products menu for option your taxonomy labeled Type, as shown in Figure 7-3. Clicking this new menu item brings you to the custom taxonomy admin panel for types, shown in Figure 7-4. This admin panel works exactly as the post categories admin panel does. Here you can create new taxonomy terms, edit and delete existing terms, fi nd how many products are assigned to each, and also search taxonomy terms.

    FIGURE 7-4: Custom taxonomy admin panel

    c07.indd 129 12/6/12 1:31 AM 130 ❘ CHAPTER 7 CUSTOM POST TYPES, CUSTOM TAXONOMIES, AND METADATA

    The fi nal new item added for your custom taxonomy is a meta box on the product edit screen, shown in Figure 7-5. To view this, click Add New Product. The meta box appears on the right side of your screen and looks very similar to the Category meta box. Here you can easily add and delete new types on your products. As with custom post types, you can set a variety of different arguments when registering a custom taxonomy:

    ➤ public — Sets whether a custom taxonomy is publicly available on the admin dashboard or front-end of your website. By default, this is set to true. The default settings for show_ui and show_in_nav_menus are inherited from this setting. ➤ show_ui — Sets whether to create a default UI in the WordPress admin dashboard for managing this taxonomy. Defaults to the value defi ned by the public argument. ➤ show_in_nav_menus — Sets whether the post type is FIGURE 7-5: Custom taxonomy available for selection in the menu management feature of meta box WordPress. Defaults to the value defi ned by the public argument. ➤ show_tagcloud — Sets whether to allow the built-in Tag Cloud widget to use this taxonomy. Defaults to the value defi ned by the show_ui argument. ➤ hierarchical — Sets whether this custom taxonomy is hierarchical (like categories) or not hierarchical (like tags). By default, this argument is set to false. ➤ update_count_callback — Function name that will be called when a term in your taxonomy gets a count update. The default value is none. ➤ query_var — Enables the public query var for the taxonomy. Acceptable values are true, false, or a string to set a custom query var value. ➤ rewrite — The rewrite argument sets the URL parsing rules for permalinks referring to this taxonomy. This allows you to customize the taxonomy slug in your URL. This argument can be set to true, false, or an array of values. If passing an array, it accepts the following values. By default this argument is set to true and the $taxonomy name is used as the slug. ➤ slug — Set a custom permalink slug. Defaults to the taxonomy name value. ➤ with_front — Sets whether your taxonomy should use the front base from your permalink settings. For example, if you prefi xed your permalinks with /blog, and with_front is set to true, your taxonomy permalinks would include /blog at the beginning. ➤ hierarchical — Allow hierarchical URLs. Defaults to false. To learn more about the register_taxonomy() function, visit the offi cial Codex page at http:// codex.wordpress.org/Function_Reference/register_taxonomy.

    c07.indd 130 12/6/12 1:31 AM Building Your Own Taxonomies ❘ 131

    Setting Custom Taxonomy Labels Similar to creating a custom post type in WordPress, custom taxonomies feature several text strings that are shown throughout the WordPress admin dashboard for your taxonomy. These text strings are typically a link, button, or extra information about the custom taxonomy. By default, the term “Tag” is used for non-hierarchical taxonomies and “Category” for hierarchical taxonomies. The available labels for your custom taxonomy include:

    ➤ name — General name for the taxonomy, which is usually plural. ➤ singular_name — The singular version of the name for the taxonomy. ➤ search_items — Text for the search items button. ➤ popular_items — Label for popular items text. ➤ all_items — Label for all items text. ➤ parent_item — The parent item text. Not used on non-hierarchical taxonomies. ➤ parent_item_colon — Same as parent_item, but with a colon at the end. ➤ edit_item — Used as the text for editing an individual taxonomy term. ➤ update_item — Used as the text for updating an individual taxonomy term. ➤ add_new_item — Text for creating a new taxonomy term. ➤ new_item_name — The new item text name text. ➤ separate_items_with_commas — The separate items with commas text used in the taxonomy meta box. Not used on hierarchical taxonomies. ➤ add_or_remove_items — Text displayed in the taxonomy meta box when JavaScript is disabled. Not used on hierarchical taxonomies. ➤ choose_from_most_used — The choose from most used text used in the taxonomy meta box. Not used on hierarchical taxonomies. ➤ menu_name — The menu name text. Defaults to the value of name. Setting these labels makes it much easier on users when administering custom taxonomy terms. Now modify the custom taxonomy registration code from earlier with custom labels:

    function prowp_define_product_type_taxonomy() {

    $labels = array( 'name' => 'Type', 'singular_name' => 'Types', 'search_items' => 'Search Types', 'all_items' => 'All Types', 'parent_item' => 'Parent Type', 'parent_item_colon' => 'Parent Type:',

    c07.indd 131 12/6/12 1:31 AM 132 ❘ CHAPTER 7 CUSTOM POST TYPES, CUSTOM TAXONOMIES, AND METADATA

    'edit_item' => 'Edit Type', 'update_item' => 'Update Type', 'add_new_item' => 'Add New Type', 'new_item_name' => 'New Type Name', 'menu_name' => 'Type' );

    $args = array( 'labels' => $labels, 'hierarchical' => true, 'query_var' => true, 'rewrite' => true );

    register_taxonomy( 'type', 'products', $args );

    } ?>

    Using Your Custom Taxonomy Now that you’ve created your custom taxonomy, you need to know how to use it on your website. As always, WordPress features some very easy-to-use functions for working with your custom taxonomy. The following shows how you can display a tag cloud showing your custom taxonomy terms:

    'type', 'number' => 5 ) ); ?>

    The wp_tag_cloud() function can accept many different arguments, but in this example, you’re using only two: taxonomy and number. First, you set your taxonomy to type; this tells WordPress to return only taxonomy terms defi ned under the custom taxonomy you created for types. Next, you defi ne the number of terms you want to display, which in this example is 5. Calling this function in your theme sidebar displays a nice tag cloud that shows the fi ve taxonomy terms with the most products assigned to them.

    You can also create a custom Loop using WP_Query to display products for a specifi c taxonomy term. Say you want to create a custom Loop to display only products that are in the weapon type:

    'products', 'tax_query' => array( array( 'taxonomy' => 'type', 'field' => 'slug', 'terms' => 'weapon' ) ) );

    $products = new WP_Query( $args );

    while ( $products->have_posts() ) : $products->the_post();

    c07.indd 132 12/6/12 1:31 AM Metadata ❘ 133

    echo '

    ' .get_the_title(). '

    '; endwhile;

    wp_reset_postdata(); ?>

    That’s it! The two WP_Query arguments you send are the post_type, products in this case, and the tax_query, which specifi es which taxonomy term to use. You can also easily display custom taxonomy terms assigned to each post. To do this, you’ll be using the get_the_term_list() WordPress function. This function works very similarly to get_the_ tag_list() but is for building a custom taxonomy term list instead.

    ID, 'type', 'Product Type: ', ', ', '' ); ?>

    The preceding code displays all custom taxonomy terms assigned to the post you are viewing. This code does need to be in the Loop in your theme template fi le to work correctly. To execute the function, you send in the post ID, custom taxonomy name, and the title you want displayed next to the terms. Remember that you can always visit the function reference to learn more about this function and what parameters are allowed: http://codex.wordpress.org/Function_Reference/ get_the_term_list.

    The get_terms() function can also be used to retrieve an array of your custom taxonomy values. In the following example, you retrieve all of the terms for your type taxonomy and loop through the values displaying the term name:

    ' .$term->name. '

    '; } ?>

    Keep in mind that you need to make sure the taxonomy is defi ned before you start working with custom taxonomy values. If any of the preceding examples return blank, that means they were executed before your register_taxonomy() function was called to defi ne your custom taxonomy. Defi ning custom taxonomies in WordPress is a very powerful way to organize your website content. Using the preceding methods can help transform your website into a content management system using the power of WordPress.

    METADATA

    In this chapter, you’ve learned how to create custom post types to add to the basic content types managed by WordPress, and custom taxonomies to organize and collect those content types. This chapter wraps up with a look at extending the content management descriptors of a post with custom metadata.

    c07.indd 133 12/6/12 1:31 AM 134 ❘ CHAPTER 7 CUSTOM POST TYPES, CUSTOM TAXONOMIES, AND METADATA

    What Is Metadata? Metadata in WordPress refers to additional pieces of data attached to a post. For example, your products custom post type might need a price stored with each Product entered. The price could be stored as metadata and easily displayed on the Product detail page. Post metadata is often referred to as Custom Fields in WordPress terminology. This is a more user-friendly term in the admin dashboard of WordPress. WordPress adds a Custom Fields meta box on the post-editing screen by default, as shown in Figure 7-6. If a custom post type has the custom-fields value defi ned for the supports argument, this meta box will also appear.

    FIGURE 7-6: Custom Field meta box

    All post metadata is stored in the wp_postmeta table in your WordPress database.

    Adding Metadata WordPress features a simple function to add new post metadata called add_post_meta(). This function will attach a piece of metadata to the post specifi ed as follows:

    This function accepts the following four parameters: 1. $post_id — The ID of the post to add metadata. 2. $meta_key — The name of the metadata fi eld. 3. $meta_value — The value of the metadata fi eld. 4. $unique — A value identifying whether or not the key should be unique. The default value is false. Now that you understand the parameters for the add_post_meta() function, you can use it to add some metadata to your products.

    add_post_meta( 420, 'prowp_price', '34.99', true );

    This code example adds a metadata entry called prowp_price with a value of 34.99 to product ID 420. You also set the $unique value to true, which means there cannot be multiple entries for the

    c07.indd 134 12/6/12 1:31 AM Metadata ❘ 135

    prowp_price fi eld on this product. Now if you edit the product in WordPress, you will see a prowp_ price fi eld and value in the custom fi elds meta box.

    NOTE To prevent metadata keys from appearing in the Custom Fields meta box on the Post Edit screen, prefi x the meta key with an underscore like _prowp_ price. This will hide the data from the user and is common practice when creating custom meta boxes.

    Updating Metadata As easy as it is to add new metadata to a post, you can also update metadata using the update_ post_meta() function. This function will update a piece of metadata attached to a post specifi ed, as shown here. If the meta key does not already exist, the function will create it.

    This function accepts the following parameters:

    ➤ $post_id — The ID of the post to update metadata. ➤ $meta_key — The name of the metadata fi eld. ➤ $meta_value — The value of the metadata fi eld. ➤ $prev_value — The old value of the metadata fi eld to update. This is to differentiate between several fi elds with the same key and is an optional fi eld. For an example, you can update the price on your product from earlier as follows:

    update_post_meta( 420, 'prowp_price', '6.99' );

    The preceding code example updates the previously added metadata fi eld prowp_price to 6.99 for product ID 420.

    Deleting Metadata Now that you understand how to add and update post metadata, you can learn how to delete that data. To delete post metadata, you’ll use the delete_post_meta() function.

    This function accepts the following parameters:

    ➤ $post_id — The ID of the post to delete metadata from. ➤ $meta_key — The name of the metadata fi eld. ➤ $meta_value — The value of the metadata fi eld. This is to differentiate between several fi elds with the same key and is an optional fi eld.

    c07.indd 135 12/6/12 1:31 AM 136 ❘ CHAPTER 7 CUSTOM POST TYPES, CUSTOM TAXONOMIES, AND METADATA

    Let’s delete the post metadata you created earlier:

    delete_post_meta( 420, 'prowp_price' );

    The preceding code example will delete the prowp_price metadata from product ID 420. You did not defi ne the $meta_value parameter, so all prowp_price entries will be deleted from product ID 420.

    Retrieving Metadata You’ve covered how to add, update, and delete metadata, so now you will review how to retrieve and display metadata. WordPress makes it easy to retrieve post metadata for display or use in other code. A good place to use this code is within a Loop to display custom metadata for a particular piece of content.

    To retrieve metadata, you’ll use the get_post_meta() function:

    The function accepts these parameters:

    ➤ $post_id — The ID of the post to retrieve metadata for. ➤ $meta_key — The name of the metadata fi eld. ➤ $single — A value identifying whether to return a single meta value fi eld (true) or return an array of values (false). By default, this parameter is set to false. Let’s retrieve and display the price for your product created earlier:

    $product_price = get_post_meta( 420, 'prowp_price', true ); echo 'Price $' .$product_price;

    The product price is retrieved and displayed for product ID 420. Now assume you want to store various colors for the product. Instead of creating a separate metadata entry for each color, you’ll create an array of color entries in a single metadata fi eld:

    $product_colors = get_post_meta( 420, 'prowp_colors', false );

    echo '

      ';

      foreach ( $product_colors as $color ) { echo '

    • ' .$color .'
    • '; }

      echo '

    '; ?>

    c07.indd 136 12/6/12 1:31 AM Summary ❘ 137

    First you have to create the metadata entries for the product colors. This is done using the add_post_meta() function. Next, set the meta key name to the same and the $unique parameter to false, which will allow multiple entries under the same meta key.

    Next, you’ll use the get_post_meta() function to retrieve the product colors you just set. Notice the $single parameter is set to false, which allows you to return all entries for prowp_colors for product ID 420 as an array. Finally, you’ll loop through the colors array and display each product color.

    Another powerful function for retrieving post metadata is the get_post_custom() function. This function returns a multidimensional array of all metadata for a particular post.

    This function accepts a single required parameter — $post_id — the ID of the post whose custom fi elds will be retrieved. Let’s retrieve and display all metadata entries for your product:

    foreach( $product_metadata as $name => $value ) {

    echo '' .$name .' => ';

    foreach( $value as $nameAr => $valueAr ) { echo '
    ' .$nameAr." => "; echo var_dump( $valueAr ); }

    echo '
    ';

    } ?>

    The preceding code example will retrieve all metadata for product ID 420. Because the value returned is a multidimensional array, you have to do multiple loops to display all of the data. If you are retrieving multiple pieces of metadata for a post, this is the optimized method because it retrieves all metadata in a single database query instead of running separate queries for each piece of data requested. As you can tell, this is a more advanced method for retrieving post metadata.

    SUMMARY

    It’s very easy to see how using a combination of custom post types, custom taxonomies, and metadata in WordPress opens the doors to endless possibilities. These features have morphed WordPress from a simple blogging platform into a full-fl edged content management system capable of handling any type of data you can conceive. In the next chapter you’ll dive into creating custom plugins for WordPress. You’ll learn the proper ways to integrate into various areas of WordPress, understanding data validation to develop secure code, and even how to publish your plugins to the WordPress.org Plugin Directory.

    c07.indd 137 12/6/12 1:31 AM c07.indd 138 12/6/12 1:31 AM 8 Plugin Development

    WHAT’S IN THIS CHAPTER?

    ➤ Creating plugin fi les ➤ Data validation and plugin security ➤ Using WordPress fi lter and action hooks ➤ How to properly use the Settings API ➤ Creating a widget and dashboard widget ➤ Creating custom shortcodes ➤ Supporting language translation ➤ Publishing a plugin to the offi cial Plugin Directory

    WROX.COM CODE DOWNLOADS FOR THIS CHAPTER The wrox.com code downloads for this chapter are found at www.wrox.com/remtitle .cgi?isbn=9781118442272 on the Download Code tab. The code is in the Chapter 8 download fi le and individually named according to the code fi le names throughout the chapter. One of the main reasons WordPress is such a popular software platform is the ease with which it can be extended. Plugins are the primary reason for this and allow endless possibilities in extending WordPress. This chapter discusses everything you need to know to create amazing plugins in WordPress. You are going to look at plugins from both a functional and structural perspective. Starting with the packaging of plugin fi les, you’ll dig into the API hooks that connect your custom plugin code to the WordPress core and show how to integrate a plugin into various parts of the WordPress editing, management, and display processes. Finally, you will see how to publish a plugin for others to use. At the end of this chapter, you build a WordPress plugin from the

    c08.indd 139 12/6/12 1:19 AM 140 ❘ CHAPTER 8 PLUGIN DEVELOPMENT

    ground up. You’ll utilize many of the features discussed in this chapter and learn the proper way to extend WordPress through a custom plugin.

    PLUGIN PACKAGING

    When developing plugins in WordPress, it’s best to follow a standard plugin packaging template — that is, certain functional and descriptive components that will exist in all plugins you create for WordPress. This chapter discusses the requirements for a plugin, as well as recommended additions such as software license and internationalization. While the actual code implementation of the plugin is the exciting part of the process, consider the plugin packaging similar to elementary grammar rules for a new language: necessary for making yourself understood.

    Creating a Plugin File The fi rst step in creating a WordPress plugin is to create a new PHP fi le for your plugin code. The plugin fi le name should be descriptive of your plugin so it’s easy to identify your plugin in the plugins directory. It should also be unique because all WordPress plugins exist in the same folder. If your plugin fi le name is too generic, you run the risk of another plugin having the same fi le name, which would be an obvious problem. A plugin can also exist in a folder containing all of the necessary fi les the plugin needs to run. A folder should always be used because it helps keep the user’s plugin folder organized. It’s also a good idea to maintain a clean folder structure, which refers to keeping all similar fi les together. For example, if your plugin includes images, you should create an /images folder inside your plugin folder to store any custom images your plugin might use. Let’s look at a standard folder structure for a plugin:

    ➤ /unique-plugin-name (no spaces or special characters) ➤ unique-plugin-name.php — Primary plugin PHP fi le ➤ uninstall.php — The uninstall fi le for your plugin ➤ /js — Folder for JavaScript fi les ➤ /css — Folder for style sheet fi les ➤ /includes — Folder for additional PHP includes ➤ /images — Folder for plugin images Keeping your fi les organized using a clean folder structure can make it much easier to track the fl ow of your plugin over time.

    Creating the Plugin Header A requirement for all WordPress plugins is a valid plugin header. The plugin header must be defi ned at the very top of your main PHP fi le as a PHP comment. It does not need to exist in every fi le for your plugin, only the main PHP fi le. This header tells WordPress that your PHP fi le is in fact a

    c08.indd 140 12/6/12 1:19 AM Plugin Packaging ❘ 141

    legitimate WordPress plugin and should be processed as such. Following is an example of a standard plugin header: The only required line in the plugin header is the Plugin Name. The rest of the information is optional but highly recommended. The information FIGURE 8-1: Example plugin listing listed in your plugin header is used on the Manage Plugins section of WordPress. You can see what the header looks like in WordPress in Figure 8-1. You can see how important the plugin header information is, including all optional data. The information should be accurate and provide good links to your website and the plugin URI for additional information and support regarding your plugin.

    Plugin License When developing a plugin you plan on releasing to the public, it’s customary to include the software license that the plugin is released under just below your plugin header. This is not a requirement for the plugin to function, but is a good idea to clearly state what software license your plugin uses. A license comment block will also state that there is no warranty, which protects you from liability should someone decide your plugin destroyed his or her site. Following is a standard GPL license, under which most WordPress plugins are released:

    This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ ?>

    c08.indd 141 12/6/12 1:19 AM 142 ❘ CHAPTER 8 PLUGIN DEVELOPMENT

    To use this license in your plugin fi ll in the year, plugin author name, and plugin author e-mail in the preceding comment. By doing so your plugin will be licensed under the GPL. WordPress is licensed under the GPLv2 software license. This is a very common software license for open source projects. Since plugins are dependent on WordPress to function, they should also be released under a GPL, or compatible, software license. For more information on GPL licensing visit http://www.gnu.org/licenses/licenses.html.

    Activating and Deactivating Functions You’ll want to utilize some important functions when creating plugins. The fi rst of these is called the register_activation_hook() function. This function is executed when your plugin is activated in the WordPress Plugins SubPanel. The function accepts two parameters: the path to the main plugin fi le and the function to execute when the plugin is activated.

    In most of the code examples in this chapter, you’re going to use prowp as a function and variable prefi x, as well as a descriptive name for your plugin. It’s just an example short name, but one that you’re going to see in a lot of code. The following example executes the function prowp_install() when the plugin is activated:

    function prowp_install() { //do something } ?> This is an extremely useful function if you need to execute any actions when your plugin is activated. For example, you may want to check the current WordPress version to verify that your plugin is compatible. You may also want to create some default option settings. One important check you should always do when your plugin is activated is to verify that the version of WordPress the user is running is compatible with your plugin. This ensures any functions, hooks, and so on that your plugin uses are available in WordPress.

    function prowp_install() { global $wp_version;

    if ( version_compare( $wp_version, '3.5', '<' ) ) {

    wp_die( 'This plugin requires WordPress version 3.5 or higher.' );

    } } ?>

    The preceding function uses the global variable $wp_version, which stores the currently running version of WordPress, and verifi es that it is not running a version lower than 3.5. You do the version comparison using the version_compare() PHP function. If the WordPress version is lower than 3.5, you display an error message to the users that they need to update.

    c08.indd 142 12/6/12 1:19 AM Plugin Packaging ❘ 143

    There is also a function that executes when a plugin is deactivated called register_deactiva- tion_hook(). This function is executed when your plugin is deactivated in the WordPress Plugins SubPanel. This function accepts the same two arguments as the register_activation_hook function. Following is an example using the deactivation function:

    function prowp_deactivate() { //do something } ?>

    NOTE It’s important to remember that deactivating is not uninstalling. You should never include uninstall functionality in your deactivation function. Imagine that a user accidentally deactivates your plugin and all of their settings are deleted. That would not be a good user experience and should be avoided.

    Internationalization Internationalization, sometimes shortened to “i18n” in the WordPress Codex, is the process of making your plugin or theme ready for translation, or localized. In WordPress, this means marking strings that should be translated. Localization is the process of translating the text displayed by the theme or plugin into different languages. This isn’t a requirement, but internationalization should be used on any plugin you plan on distributing. This opens up your plugin to the widest possible audience.

    WordPress features many different functions to make a string translatable. The fi rst function is __ (). That isn’t a typo; the function is two underscores, as shown here: The fi rst parameter you pass is the string that you want to be translated. This string is what will be displayed to the browser if the text is not translated into a different language. The second parameter is the text domain. In the case of themes and plugins, the domain should be a unique identifi er, which is used to distinguish between all loaded translations.

    If your code should echo the translatable string to the browser, you’ll want to use the _e() function, as shown here:

    This function works exactly the same as __(); the only difference is that the value is echoed to the browser. Placeholders need special consideration when internationalizing your plugins and themes. As an example, look at an error message you want to make translatable: Error Code 6980: Email is a required field

    c08.indd 143 12/6/12 1:19 AM 144 ❘ CHAPTER 8 PLUGIN DEVELOPMENT

    The obvious, but incorrect, way to attempt to split a string into translatable parts is to separate the fi eld name, error number, and descriptive string:

    echo $error; ?> This is actually the wrong way to include dynamic values in your translatable string because your translatable string is cut into two parts. These two parts may not work independently in another language. This could also seriously confuse the translator viewing a bunch of cryptic phrases that mean nothing when separated. The proper way is shown here:

    As you can see, this uses the PHP printf() function, which outputs the formatted string. Your two variables are passed to printf() and inserted into the string in the designated spots. In this example, a developer translating your plugin messages into another language would see the line as Error Code %1$d: %2$s is a required field and know it’s possible to move around the error number and fi eld values to make sense in the target language. Splitting the strings leads to split translations and possibly unintentionally funny translated grammar. Alternatively, you could use the PHP sprintf() function if you want to store the error message value in a variable prior to displaying it. Plurals also need special consideration when defi ning your translatable strings. Say you need to translate a string like this: This works great if you have one new message, but what if you have more than one new message? Fortunately, WordPress contains a function you can use to handle this problem called _n(). The following code shows it in action: This function accepts four parameters: the singular version, the plural version, the actual number, and the domain text for your plugin. The _n() function uses the number parameter ( $count in the example) to determine whether the singular or plural string should be returned.

    c08.indd 144 12/6/12 1:19 AM Plugin Packaging ❘ 145

    WordPress also features a translation function you can use to add comments to your translatable strings. This is helpful if you have a string set up for translation that might have multiple meanings. To do this, you use the _x() function, as shown in the following code: As you can see, there are three parameters for this function. The fi rst is the text string to translate. The second, and most important, is the context information for the translators. This allows you to add custom comment messages that the translator can read to explain the context of your text to be translated. The fi nal parameter is the text domain. Now that you’ve prepared your plugin for translation, you must load the localization fi le to do the translation. To do so, you execute the load_plugin_textdomain() function as shown here:

    function prowp_init() { load_plugin_textdomain( 'prowp-plugin', false, plugin_basename( dirname( __FILE__ ) .'/localization' ) ); } ?> The fi rst parameter you pass is the domain text name that you’ve used to identify all of your translatable strings. The second parameter is the path relative to the ABSPATH variable; however, this parameter is now deprecated in favor of the third parameter. The fi nal parameter is the path to your translation fi les from the /plugins directory. To store these fi les, you should create a folder inside your plugin directory called /localization . You use the plugin_basename() and dirname() functions to retrieve the path to your localization folder. You can learn more about the process of creating translation fi les in the WordPress Codex at http://codex.wordpress.org/I18n_for_WordPress_Developers.

    Determining Paths When creating WordPress plugins, you will often need to reference fi les and folders throughout the WordPress installation. Since WordPress 2.6, users have had the ability to move this directory any- where they want. Because of this, you should never use hard-coded paths in a plugin. WordPress has a set of functions to determine the path to the wp-content and plugins directories, as well as directories within your plugins. You can use these functions in your plugins to verify that any paths you are referencing are correct regardless of where the actual directory might exist on the server.

    Local Paths To determine the local server path to your plugin, you’ll use the plugin_dir_path() function. This function extracts the physical location relative to the plugins directory from its fi le name.

    c08.indd 145 12/6/12 1:19 AM 146 ❘ CHAPTER 8 PLUGIN DEVELOPMENT

    You can see that you pass the __FILE__ PHP constant to the plugin_dir_path() function. This returns the full local server path to your plugin directory: /public_html/wp-content/plugins/halloween-plugin/ Now let’s assume you need to reference the local path to a fi le in a subdirectory in your plugin. You can use the plugin_dir_path() function along with the subdirectory and fi les you want to reference, as shown here: The preceding example would produce the following result: /public_html/wp-content/plugins/halloween-plugin/js/script.js URL Paths To determine the full URL to any fi le in your plugin directory, you’ll use the plugins_url() function as shown here:

    You can see the plugins_url() function accepts two parameters. The fi rst parameter is the path relative to the plugins URL. The second parameter is the plugin fi le that you want to be relative to. In this case, you’ll use the __FILE__ PHP constant. The preceding example will return a full URL to your plugin’s icon.png fi le located in the images directory, as shown here:

    The following is a list of the many advantages of using the plugins_url() function to determine plugin fi le URLs:

    ➤ Supports the /mu-plugins plugin directory ➤ Auto detects SSL. If SSL is enabled, the returned URL would contain https:// ➤ Can detect the location of the plugin even if the user has moved his /wp-content directory to a custom location ➤ Supports Multisite WordPress also features various functions to determine URLs in WordPress. The following is a list of the functions available:

    ➤ admin_url() — Admin URL (http://example.com/wp-admin/) ➤ site_url() — Site URL for the current site (http://example.com) ➤ home_url() — Home URL for the current site (http://example.com) ➤ includes_url() — Includes directory URL (http://example.com/wp-includes/) ➤ content_url() — Content directory URL (http://example.com/wp-content/) ➤ wp_upload_dir() — Returns an array with location information on the confi gured uploads directory Understanding the proper way to access fi les in your plugins is essential to ensure maximum compatibility with all WordPress installations, regardless of how customized they are.

    c08.indd 146 12/6/12 1:19 AM Plugin Security ❘ 147

    PLUGIN SECURITY

    One of the most important steps in creating a plugin is making sure it is secure from hacks and exploits. If a plugin contains security holes, it opens up the entire WordPress website for malicious hackers to wreak havoc. WordPress features some built-in security tools that you should always utilize to make sure your plugins are as secure as can be. Remember that all data external to your plugin code is suspect until proven valid. Always validate your data before displaying to the browser or inserting into the database to help keep your plugins secure from hacks and exploits. You’ll be using the mentioned escaping and sanitizing functions discussed in this section throughout the chapter.

    Nonces Nonces, which stands for “number used once,” are used in requests (saving options, form posts, Ajax requests, actions) to stop unauthorized access by generating a secret key. This secret key is generated prior to generating a request (that is, form post). The key is then passed in the request to your script and verifi ed to be the same key before anything else is processed. Now let’s look at how you can manually create and check nonces. The following example uses a nonce in a form:

    Enter your name:

    When creating a form nonce, the function wp_nonce_field() must be called inside of your

    tags. There are actually no required parameters for this function to work, but for increased security there are two parameters you should set. The fi rst parameter is $action, which should be a unique string that is descriptive of the action being performed. The second parameter is a unique name for the fi eld, $name. By default, the fi eld name will be _wpnonce, but you can defi ne a custom unique name in this parameter.

    When the wp_nonce_field() function is called, it will generate a unique secret key that will be added as a hidden form fi eld and passed with your form data. After your form is posted, the fi rst thing you need to do is check your nonce secret key using the check_admin_referer() function like so: function prowp_update_options() {

    if ( isset( $_POST['submit'] ) ) {

    //check nonce for security check_admin_referer( 'prowp_settings_form_save', 'prowp_nonce_field' );

    //nonce passed, now do stuff

    } }

    c08.indd 147 12/6/12 1:19 AM 148 ❘ CHAPTER 8 PLUGIN DEVELOPMENT

    Verifying that the nonce is valid is as simple as calling the check_admin_referer() function and passing it your unique nonce action and name that you defi ned earlier. If the nonce secret key does not match the secret key created on your form, WordPress will stop processing the page and issue an error message. This primarily protects it from cross-site request forgery, or CSRF. Nonces can also be used on links that perform actions. To create a URL nonce, you use the wp_nonce_url() function. This can be used in conjunction with multiple query strings in your URL like so: Delete

    The wp_nonce_url() function accepts two parameters: the URL to add the nonce to and the unique nonce name you are creating. The preceding code would generate a link that looks like this: http://example.com/wp-admin/my-url.php?action=delete&ID=15&_wpnonce=e9d6673015

    Notice how the _wpnonce query string is appended to the link. This is the secret key value that was generated for your URL nonce. If your URL has no query strings, the wp_nonce_url() function will add the nonce value as the only query string being passed. If your URL contains query strings, that nonce value will be added to the end of the URL. You can verify that the nonce is correct just as you did with your form — by using the check_admin_referer() function: function prowp_update_options() {

    if ( isset( $_GET['action'] ) ) {

    //check nonce for security check_admin_referer( 'prowp_nonce_url_check' );

    //do stuff } } This function verifi es that your action query string is set before checking your nonce value. Once the nonce has been validated, the script will continue. Remember that if the nonce is not validated, the page execution will stop, preventing any type of hack attempt.

    Data Validation and Sanitization Any data that comes from somewhere external to your code (such as user input) needs to be scrubbed to verify that it’s free from illegal characters and potentially unsafe data. Data validation is essential to proper plugin security. Improperly validated data can lead to SQL injection hacks, exploits, errors, and much more. WordPress features a set of escaping functions that you can use to verify that your data is escaped properly when being displayed to the screen. These escaping functions follow a set naming standard (see the following list), which makes it easy to identify what they are escaping. Figure 8-2 shows the escaping function naming template.

    c08.indd 148 12/6/12 1:19 AM Plugin Security ❘ 149

    ➤ esc_: The prefi x for the escaping functions. esc_attr_e( ) ➤ attr: The escaping context (attr, html, textarea, js, sql, url, 123 and url_raw). FIGURE 8-2: Escaping API breakdown ➤ _e: The optional translation suffi x. Available suffi xes are __ and _e.

    The esc_html() function is used for escaping data that contains HTML. This function encodes special characters into the equivalent HTML entities. These characters include &, <, >, ", and ' as follows:

    The esc_attr() function is used for escaping HTML attributes. This function should be used whenever you need to display data inside an HTML element:

    The esc_textrea() function is used for escaping HTML

    WordPress also features a function for validating URLs called esc_url(). This function should be used to scrub the URL for illegal characters. Even though the href is technically an HTML attri- bute, you should use the esc_url() function like so:

    The esc_js() function escapes text strings in JavaScript:

    The esc_sql() function escapes data for use in a MySQL query. This function is really just a shortcut for $wpdb->escape()as follows:

    The optional translation suffi x ( __ or _e) is used for translating the escaped data. The _e suffi x will echo the escaped translated text, whereas __ only returns the escaped translated value.

    //escapes, translates, but does NOT display $text = esc_html__( $text, 'prowp-plugin' ); ?>

    If the data you are validating is supposed to be an integer, use the intval() PHP function to verify that. The intval() function will return the integer value of a variable. If the variable is a string, and therefore not an integer, it will return 0. $variable = 12345; $variable = intval( $variable );

    c08.indd 149 12/6/12 1:19 AM 150 ❘ CHAPTER 8 PLUGIN DEVELOPMENT

    Another useful function for working with integers is the absint() WordPress function. This function ensures that the result is a nonnegative integer: $variable = 12345; $variable = absint( $variable ); WordPress also features some very useful sanitizing functions. These functions should be used to sanitize any data prior to saving it in the database. One of those functions is sanitize_text _field(). This function will remove all invalid UTF-8 characters, convert single < into HTML entities, and remove all HTML tags, line breaks, and extra white space.

    You can also sanitize an e-mail address using sanitize_email(). This function will strip out all characters that are not allowable in an e-mail address. Consider the following code:

    You can see that the sanitize_email() function removes the extra spaces and illegal characters from the e-mail address submitted.

    A very powerful function for processing and sanitizing untrusted HTML is wp_kses(). This function is used in WordPress to verify that only allowed HTML tags and attributes can be submitted by users. By defi ning allowed HTML tags you can avoid cross-site scripting (XSS) attacks through your code. Consider the following example: $allowed_tags = array( 'strong' => array(), 'a' => array( 'href' => array(), 'title' => array() ) );

    $html = 'link. This is bold and strong';

    echo wp_kses( $html, $allowed_tags ); The fi rst step is to defi ne an array of all HTML tags and attributes. In this example, you are allowing the and tags. The tag is allowed to include the href and title attributes. Next, you build an $html variable to test out the function. The fi nal step is to pass the $html string and $allowed_tags arguments to the wp_kses() function. The preceding example would display the following code: link. This is bold and strong

    Notice the tags have been completely removed. The function also removed the class attribute from the tag because you didn’t specify that as an allowed attribute. This basic example really shows the power of this function. Any time you need to allow users to input HTML code, you should always use the wp_kses() function to verify that only acceptable HTML tags and attributes are allowed.

    c08.indd 150 12/6/12 1:19 AM Know Your Hooks: Actions and Filters ❘ 151

    For more information on data validation in WordPress, check out the following Codex article: http://codex.wordpress.org/Data_Validation.

    NOTE Throughout this chapter, you’ll be using various data validation techniques in the code examples. The goal of this is to stress the importance of keeping security in the front of your mind when developing plugins for WordPress.

    KNOW YOUR HOOKS: ACTIONS AND FILTERS

    One of the most important features for extending WordPress is called a hook. Hooks are simply a standardized way of “hooking” into WordPress. Using hooks, you can execute functions at specifi c times in the WordPress process, allowing you to alter how WordPress functions and the expected output. Hooks are the primary way plugins interact with your content in WordPress. Up to this point, you’ve focused on the structure and format of plugins, but now you’re actually going to make a plugin do something! A hook is simply a PHP function call with various parameters that can be sent. Following is an example showing a properly formatted Action hook call: Actions and Filters Two types of hooks can be used: actions and fi lters. Action hooks are triggered by events in WordPress. For example, an Action hook is triggered when a new post is published. Filter hooks are used to modify WordPress content before saving it to the database or displaying it to the screen. For example, a Filter hook is available for the content of the post or page. This means you can alter that content after it is retrieved from the database but before it is displayed in your browser. Look at an example of a Filter hook in action. Remember that Filter hooks modify content, so this example modifi es the post content:

    The add_filter() function is used to execute a Filter action. You are using the fi lter called the_ content, which is the fi lter for your post content. This tells WordPress that every time the content is displayed, it needs to pass through your custom function called prowp_function(). The add_filter() function can accept four parameters: 1. filter_action (string): The fi lter to use 2. custom_filter_function (string): The custom function to pass the fi lter through 3. priority (integer): The priority in which this fi lter should run. When multiple callback functions are attached to the same hook, the priority parameter determines the execution order 4. accepted args (integer): The number of arguments the function accepts

    c08.indd 151 12/6/12 1:19 AM 152 ❘ CHAPTER 8 PLUGIN DEVELOPMENT

    Here’s an example of the_content fi lter in action:

    function prowp_profanity_filter( $content ) {

    $profanities = array( 'sissy', 'dummy' ); $content= str_ireplace( $profanities, '[censored]', $content );

    return $content;

    } ?>

    The prowp_profanity_filter() function will replace the words “sissy” and “dummy” with [censored] automatically on all posts and pages on your website. You are using the str_ireplace() PHP function to handle the replacement. This function will replace some characters in a string with other characters in a string. The str_ireplace() function is also case-insensitive. Because you are using a Filter hook, the content isn’t actually modifi ed in the database; instead, it’s modifi ed during processing of the_post(), before being displayed, when this fi lter is invoked. The content in the database is not affected so the words “sissy” and “dummy” will still exist in your content, and if you ever disable or change the plugin, those words will appear in the displayed text. Filter hooks always receive data; in this case, the $content variable is passed to your function and contains your post content. Also notice the last line of your function returns the $content variable. Remember that you must always return the content you are modifying or else it returns empty and therefore displays nothing. Now that you’ve seen the Filter hook in action, take a look at the Action hook and what it can do. The Action hook is triggered by events in WordPress. WordPress doesn’t require any return values from your Action hook function; the WordPress Core just notifi es your code that a specifi c event has taken place. The Action hook is structured exactly like a Filter hook, as you can see in the following code:

    The add_action() function accepts four parameters just like the add_filter() function. Here you can set the hook name you want to hook into, the custom function name you are going to execute when the event is triggered, and the priority and the number of accepted args. Here’s a real example using an Action hook:

    function prowp_email_new_comment() { wp_mail( '[email protected]', 'New blog comment', 'There is a new comment on your website: http://example.com' ); } ?>

    Notice that you are using the comment_post Action hook. This action is triggered whenever a new comment is posted in WordPress. As you can see, the prowp_email_new_comment() function will send an e-mail any time a new comment is created. Also notice that you are not sending in any

    c08.indd 152 12/6/12 1:19 AM Know Your Hooks: Actions and Filters ❘ 153

    variables to your function or returning any values out of your function. Action hooks don’t require this, but if needed, you can pass values into your function.

    Popular Filter Hooks More than 1,500 different hooks are available in WordPress, which is a bit overwhelming at fi rst. Fortunately, a handful of them are used much more often than the rest. This section explores some of the more commonly used hooks in WordPress. Some of the more common Filter hooks are:

    ➤ the_content — Applied to the content of the post or page before displaying ➤ the_content_rss — Applied to the content of the post or page for RSS inclusion ➤ the_title — Applied to the post or page title before displaying ➤ comment_text — Applied to the comment text before displaying ➤ wp_title — Applied to the page before displaying ➤ the_permalink — Applied to the permalink URL Let’s look at some of the more popular Filter hooks in WordPress, starting with a more practical example than your profanity fi lter, which uses the_content Filter hook. This hook allows you to alter the content for posts and pages prior to it being displayed in the browser. By using this hook you can add your custom content either before, in the middle, or after the content: <?php add_filter ( 'the_content', 'prowp_subscriber_footer' );</p><p> function prowp_subscriber_footer( $content ) {</p><p> if( is_single() ) {</p><p>$content.= '<h3>Enjoyed this article?</h3>'; $content.= '<p>Subscribe to my <a href="http://example.com/feed">RSS feed</a>!</p>'; }</p><p> return $content; } ?> In this example, you are adding your subscribe text to the bottom of the content of your posts. Notice that you are also using the is_single() conditional tag to verify that your subscribe text is added only on a single post page. The $content variable stores all of the post or page content, so by appending your subscribe text you are adding it to the bottom of your post content. This is the ideal way to add content to the bottom of all posts because you aren’t actually modifying the post. In the future, if you decide to change this message you can change it in one place, rather than updating every post in your website.</p><p> c08.indd 153 12/6/12 1:19 AM 154 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>Another powerful Filter hook is the_title. This hook is used for changing the post or page title prior to being displayed. Here’s an example that uses this fi lter: <?php add_filter( 'the_title', 'prowp_custom_title' );</p><p> function prowp_custom_title( $title ) {</p><p>$title .= ' - By Example.com'; return $title;</p><p>} ?> This example adds “By Example.com” to all of your post and page titles. Remember that this doesn’t actually modify the title in the database but instead modifi es the display of the title generated for the end user.</p><p>The default_content Filter hook is useful for setting the default content when creating a new post or page. This is helpful if you have a set format for all of your posts as it can save you valuable writing time: <?php add_filter( 'default_content', 'prowp_default_content' );</p><p> function prowp_default_content( $content ) {</p><p>$content = 'For more great content please subscribe to my RSS feed'; return $content;</p><p>} ?> Filter hooks are exceptionally powerful for inserting your own processing into a variety of points in the Loop processing of each post. Realizing the full power of the WordPress plugin system means also using action hooks to fi re your own code in response to events within the WordPress core.</p><p>Popular Action Hooks Some of the more common Action hooks are:</p><p>➤ publish_post — Triggered when a new post is published. ➤ create_category — Triggered when a new category is created. ➤ switch_theme — Triggered when you switch themes. ➤ admin_head — Triggered in the <head> section of the admin dashboard. ➤ wp_head — Triggered in the <head> section of your theme. ➤ wp_footer — Triggered in the footer section of your theme usually directly before the </body> tag. ➤ init — Triggered after WordPress has fi nished loading, but before any headers are sent. Good place to intercept $_GET and $_POST HTML requests.</p><p> c08.indd 154 12/6/12 1:19 AM Know Your Hooks: Actions and Filters ❘ 155</p><p>➤ admin_init: Same as init but only runs on admin dashboard pages. ➤ user_register: Triggered when a new user is created. ➤ comment_post: Triggered when a new comment is created.</p><p>One of the most commonly used Action hooks is the wp_head hook. Using the wp_head hook, you can insert any custom code into the <head> section of the WordPress theme. Consider the following example: <?php add_action( 'wp_head', 'prowp_custom_css' );</p><p> function prowp_custom_css() { ?> <style type="text/css"> a { font-size: 14px; color: #000000; text-decoration: none; } a:hover { font-size: 14px color: #FF0000; text-decoration: underline; } </style> <?php } ?></p><p>This code will drop anything inside your prowp_custom_css() function into the header of the WordPress theme, in this case your custom CSS script.</p><p>The wp_footer hook is also a very commonly used Action hook. Using this hook you can insert any custom code in the footer of the WordPress theme. This is a great method for adding analytic tracking code to your website: <?php add_action( 'wp_footer', 'prowp_site_analytics' );</p><p> function prowp_site_analytics() { ?> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + 'google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-XXXXXX-XX"); pageTracker._trackPageview(); </script> <?php } ?></p><p> c08.indd 155 12/6/12 1:19 AM 156 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>In the preceding example you can see how you can easily insert your Google Analytics tracking code to the footer of every page on your website.</p><p>The admin_head Action hook is very similar to the wp_head hook, but rather than hooking into the theme header, it hooks into the admin dashboard header. This is useful if your plugin requires custom CSS on the admin dashboard, or any other custom header code.</p><p>The user_register Action hook is executed when a new user is created in WordPress. This user can be created by an admin or by the new user. This is a useful hook if you want to set some default values for a new user or to e-mail your new members thanking them for joining your website. Hooks are probably one of the most under-documented features in WordPress. It can be a real challenge fi nding the correct hooks to use for the job. The fi rst resource to use is always the Codex. Here you can fi nd the Filter Reference (http://codex.wordpress.org/Plugin_API/ Filter_Reference) and Action Reference (http://codex.wordpress.org/Plugin_API/Action_ Reference) sections helpful in tracking down appropriate hooks.</p><p>Another highly recommended reference is the Plugin Directory (http://wordpress.org/extend/ plugins/) on WordPress.org. Sometimes the best way to fi gure something out is to see how other developers accomplished a similar task. Find a plugin in the directory that is similar in functionality to what you want to build. Most likely, the plugin author will have already dug up the correct hooks for WordPress that you will be using. It never hurts to learn by example, and published plugins are the perfect examples in this case!</p><p>PLUGIN SETTINGS</p><p>Most plugins feature a settings page. This helps users confi gure the plugin to act in different ways without actually modifying the code behind the plugin by saving various option settings. The fi rst step in this process is saving and retrieving options in WordPress.</p><p>Saving Plugin Options Chances are that, when building a plugin, you will need to save some options for your plugin. WordPress features some very easy-to-use functions to save, edit, and delete options. Two functions are available for creating options: add_option() and update_option(). Both functions create options, but update_option() also updates the option if it already exists. Here’s an example of adding a new option: <?php add_option( 'prowp_display_mode', 'Fright Night' ); ?></p><p>The fi rst parameter you send to the add_option() function is the name of your option. This is a required fi eld and must be unique from all other options saved in WordPress, including from other plugins. The second parameter is the option value. This is also a required fi eld and can be a string, an array, an object, or a serialized value. You can also use update_option() to create new options. This function checks whether the option exists fi rst, and if not creates it. If, however, the option already exists, it updates the value with the new option value you are sending in. You call the update_option() function exactly as you did when adding an option like so: <?php update_option( 'prowp_display_mode', 'Fright Night' ); ?></p><p> c08.indd 156 12/6/12 1:19 AM Plugin Settings ❘ 157</p><p>Generally, the update_option() function is used for both adding and updating options in plugins. It’s much easier to stay consistent with one function call for both rather than calls to different functions for adding and updating your plugin options.</p><p>Retrieving an option value is just as easy. To retrieve any option, use the get_option() function, as shown here: <?php echo get_option( 'prowp_display_mode' ); ?></p><p>The only required fi eld for get_option() is the name of the option you want to retrieve. If the option exists, it is returned to display or it is stored in a variable. If the option doesn’t exist, the function returns FALSE.</p><p>Options can be deleted as easily as they are created. To delete an option, use the delete_option() function. The only parameter is the option name that you want to delete: <?php delete_option( 'prowp_display_mode' ); ?></p><p>A good rule of thumb is to start all of your option names with the same prefi x, like prowp_ in the preceding examples. This is useful for a couple of reasons: uniqueness and readability. Using a prefi x will help validate the uniqueness of your option names. If you have a number of options, it is a smart idea to store them in an array (see the next section). This also makes it much easier to follow your code logic when there is a set naming convention used on variables, functions, and so on. Options in WordPress are not reserved for just plugins. Themes can also create options to store specifi c theme data. Many of the themes available today offer a settings page, enabling you to customize the theme through settings rather than code.</p><p>Array of Options Every option you create in WordPress adds a new record to the wp_options database table. Because of this, it’s a smart idea to store your options in an array, thus creating fewer records in the database and fewer update_option() calls you need to make. <?php $prowp_options_arr = array( 'prowp_display_mode' => 'Fright Night', 'prowp_default_browser' => 'Chrome', 'prowp_favorite_book' => 'Professional WordPress', );</p><p> update_option( 'prowp_plugin_options', $prowp_options_arr ); ?> In this code, you are creating an array to store your plugin option values. So rather than call update_option() three times, and save three records in the database, you need to call it only once and save your array to the option named prowp_plugin_options. This is a small example but imagine a collection of plugins that store 50 options to the database’s options table. That would really start to clutter up your options table and would most likely slow down your website load speeds due to the repeated database query options to fetch or set those options individually.</p><p> c08.indd 157 12/6/12 1:19 AM 158 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>To retrieve the array of options, you use the same get_option() function as before: <?php $prowp_options_arr = get_option( 'prowp_plugin_options' );</p><p>$prowp_display_mode = $prowp_options_arr['prowp_display_mode']; $prowp_default_browser = $prowp_options_arr['prowp_default_browser']; $prowp_favorite_book = $prowp_options_arr['prowp_favorite_book']; ?> The next section discusses how to create a menu for your plugin settings page.</p><p>Creating a Menu and Submenus WordPress features two different ways to create a custom menu for your plugin. The fi rst thing you’ll want to decide is where to locate your options page. The options page link can be located in its own top-level menu (My Plugin Settings), or as a submenu item of an existing menu (Settings ➪ My Plugin Settings). This section explores both methods and how to confi gure each.</p><p>Creating a Top-Level Menu The fi rst method you’ll explore is creating a new top-level menu. Using a top-level menu is useful if your plugin has multiple settings pages that need to be separate. To create your own top-level menu, you’ll use the add_menu_page function, as shown here: <?php add_menu_page( page_title, menu_title, capability, menu_slug, function, icon_url, position ); ?> Here’s a breakdown of the parameters allowed:</p><p>➤ page_title — Text used for the HTML title (between <title> tags). ➤ menu_title — Text used for the menu name in the Dashboard. ➤ capability — Minimum user capability required to see menu. ➤ menu_slug — Unique slug name for your menu. ➤ function — Displays page content for the menu settings page. ➤ icon_url — Path to custom icon for menu (default: images/generic.png). ➤ position — The position in the menu order the menu should appear. By default, the menu will appear at the bottom of the menu structure.</p><p>You can also create submenu items for your new menu. You use the add_submenu_page() function to create additional submenu items: add_submenu_page( parent, page_title, menu_title, capability, menu_slug,[function] ); Create a custom menu for a plugin with multiple submenu items, as shown in Figure 8-3.</p><p> c08.indd 158 12/6/12 1:19 AM Plugin Settings ❘ 159</p><p><?php // create custom plugin settings menu add_action( 'admin_menu', 'prowp_create_menu' );</p><p> function prowp_create_menu() {</p><p>//create new top-level menu add_menu_page( 'Halloween Plugin Page', 'Halloween Plugin', 'manage_options', 'prowp_main_menu', 'prowp_main_plugin_page', plugins_url( '/images/wordpress.png', __FILE__ ) );</p><p>//create two sub-menus: settings and support add_submenu_page( 'prowp_main_menu', 'Halloween Settings Page', 'Settings', 'manage_options', 'halloween_settings', 'prowp_settings_page' ); add_submenu_page( 'prowp_main_menu', 'Halloween Support Page', 'Support', 'manage_options', 'halloween_support', 'prowp_support_page' );</p><p>} ?></p><p>First you call the admin_menu Action hook. This hook is triggered after the basic admin panel menu structure is in place. Once triggered, you call your custom function prowp_create_menu() to build your menu.</p><p>To create your menu, you call the add_menu_page() function. The fi rst two parameters set your page title and menu title. You also set the capability level to manage_options so only an admin will see this new menu. Next, you set the menu slug to propwp_main_menu, which is the unique slug for your menu. Your custom menu function name is next, in this case prowp_main_ FIGURE 8-3: Custom plugin_page. Remember that you haven’t created this function yet so when top-level menu viewing the settings page, you will get a PHP warning. Finally, you set the custom icon location to display the WordPress logo. Now that you’ve created your top-level menu, you need to create your submenu items. In this example, you are creating two submenu items: Settings and Support. To do this, you use the add_submenu_page() function. The fi rst parameter you send is the menu slug of the top-level menu you want this to fall under. Remember that you set this to prowp_main_menu, which is a unique slug for your plugin menu. Next, you set the page title and menu title just like before. You also set the access level for viewing to manage_options. You also have to create a unique menu slug for your submenu items; in this example, you’ll use a custom named value, halloween_settings and halloween_support. The fi nal value is the custom function to build the settings page for each submenu.</p><p>Adding to an Existing Menu Next, you’ll explore how to add a submenu item to an existing menu in WordPress. Most plugins have only one options page and therefore do not require an entirely separate top-level menu. To </p><p> c08.indd 159 12/6/12 1:19 AM 160 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p> accomplish this, you can add a plugin option page to any existing menu in WordPress. Add a submenu to the Setting menu: <?php add_action( 'admin_menu', 'prowp_create_settings_submenu' );</p><p> function prowp_create_settings_submenu() { add_options_page( 'Halloween Settings Page', 'Halloween Settings', 'manage_options', 'halloween_settings_menu', 'prowp_settings_page' ); } ?> WordPress features multiple functions to make adding submenus extremely easy. To add your Halloween Settings submenu you use the add_options_page() function. The fi rst parameter is the page title followed by the submenu display name. Like your other menus, you set the capability to manage_options, so the menu is viewable only by administrators. Next, you set the unique menu handle to halloween_settings_menu. Finally, you call your custom prowp_settings_page() function to build your options page. The preceding example adds your custom submenu item Halloween Settings at the bottom of the settings menu. Following is a list of the available submenu functions in WordPress. Each function can be used exactly as the preceding example; just swap out the function name called with one of the functions listed here:</p><p>➤ add_dashboard_page() — Adds submenu items to the Dashboard menu ➤ add_posts_page() — Adds submenu items to the Posts menu ➤ add_media_page() — Adds a submenu item to the Media menu ➤ add_links_page() — Adds a submenu item to the Links menu ➤ add_pages_page() — Adds a submenu item to the Pages menu ➤ add_comments_page() — Adds a submenu item to the Comments menu ➤ add_plugins_page() — Adds a submenu item to the Plugins menu ➤ add_theme_page() — Adds a submenu item to the Appearance menu ➤ add_users_page() — Adds a submenu item to the Users page (or Profi le based on role) ➤ add_management_page() — Adds a submenu item to the Tools menu ➤ add_options_page() — Adds a submenu item to the Settings menu</p><p>Now that you’ve created your menu and submenu items, you need to create an options page to dis- play your plugin confi guration.</p><p>Creating an Options Page WordPress 2.7 introduced a new Settings API that you will be using for all of the option methods you use in this section. The Settings API is a powerful set of functions to help make saving options in WordPress easy and secure. One of the major benefi ts of the Settings API is that WordPress handles the security checks, meaning you don’t need to include a nonce in your form.</p><p> c08.indd 160 12/6/12 1:19 AM Plugin Settings ❘ 161</p><p>The fi rst option page method you’ll explore is to create a unique option page for your top-level menu. Remember that when using the add_menu_page() and add_submenu_page() functions, you defi ned your menu item function name to display your options page. To create an options page, you need to create this function to display your options. First set up your plugin menu: <?php</p><p>// create custom plugin settings menu add_action( 'admin_menu', 'prowp_create_menu' );</p><p> function prowp_create_menu() {</p><p>//create new top-level menu add_menu_page( 'Halloween Plugin Page', 'Halloween Plugin', 'manage_options', 'prowp_main_menu', 'prowp_settings_page', plugins_url( '/images/wordpress.png', __FILE__ ) );</p><p>//call register settings function add_action( 'admin_init', 'prowp_register_settings' );</p><p>} ?></p><p>Notice that you’ve added a new Action hook for admin_init to execute your prowp_register_ settings() function, as shown in the following code: <?php function prowp_register_settings() {</p><p>//register our settings register_setting( 'prowp-settings-group', 'prowp_options', 'prowp_sanitize_options' );</p><p>}?></p><p>Using the Setting API’s register_setting() function, you defi ne the option you are going to offer on your plugin options page. Your settings page will have three options, but you are going to store those three options in a single options array, so you only need to register a single setting here. The fi rst parameter is the options group name. This required fi eld needs to be a group name to identify all options in this set. The second parameter is the actual option name and must be unique. The third parameter is a callback function to sanitize the option values. Now that you’ve registered your options, you need to build your options page. To do so, you’ll create the prowp_settings_page() function as called from your menu: <?php function prowp_settings_page() { ?> <div class="wrap"> <h2>Halloween Plugin Options</h2></p><p><form method="post" action="options.php"> <?php settings_fields( 'prowp-settings-group' ); ?> <?php $prowp_options = get_option( 'prowp_options' ); ?> <table class="form-table"></p><p> c08.indd 161 12/6/12 1:19 AM 162 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p><tr valign="top"> <th scope="row">Name</th> <td><input type="text" name="prowp_options[option_name]" value="<?php echo esc_attr( $prowp_options['option_name'] ); ?>" /> </td> </tr></p><p><tr valign="top"> <th scope="row">Email</th> <td><input type="text" name="prowp_options[option_email]" value="<?php echo esc_attr( $prowp_options['option_email'] ); ?>" /></td> </tr></p><p><tr valign="top"> <th scope="row">URL</th> <td><input type="text" name="prowp_options[option_url]" value="<?php echo esc_url( $prowp_options['option_url'] ); ?>" /> </td> </tr> </table></p><p><p class="submit"> <input type="submit" class="button-primary" value="Save Changes" /> </p></p><p></form> </div> <?php } ?></p><p>As you can see, this looks like a standard form with a couple of noticeable differences. The <form> tag must be set to post to options.php. Inside your form, you need to defi ne your settings group, which you set to prowp-settings-group when you registered your settings. This establishes the link between your options and their values. You do so with this line of code: <?php settings_fields( 'prowp-settings-group' ); ?></p><p>Next, you’ll load the existing options array, if there are any, to the $prowp_options variable using the get_option() function. You’ll use this variable to display the existing options that are set in your form. Then you build the table to display your form options. Notice the name of the form fi eld needs to be in the format of option_name[field_name]. This is because you are storing all option values in a single array. <input type="text" name="prowp_options[option_email]" value="<?php echo esc_attr( $prowp_options['option_email'] ); ?>" /> After you have displayed all of your form fi elds, you need to display a Submit button to post the form and save your options. The fi nal step is to create the prowp_sanitize_options() function. This function will be used to sanitize all data submitted in your plugin settings prior to saving in the </p><p> c08.indd 162 12/6/12 1:19 AM Plugin Settings ❘ 163</p><p> database. This is an extremely important step because unsanitized data could potentially open up a security vulnerability in your plugin. <?php function prowp_sanitize_options( $input ) {</p><p>$input['option_name'] = sanitize_text_field( $input['option_name'] ); $input['option_email'] = sanitize_email( $input['option_email'] ); $input['option_url'] = esc_url( $input['option_url'] );</p><p> return $input;</p><p>} ?> Notice how each option value is being sanitized with a specifi c function. The name option uses the WordPress function sanitize_text_field() to strip any HTML, XML, and PHP tags from the submitted value. You use the sanitize_email() WordPress function to sanitize the e-mail value and esc_url() to sanitize the URL value. That’s it! You have just created a very basic plugin options page using the Settings API in WordPress. Listing 8-1 shows the entire code to build an options page.</p><p>LISTING 8-1: Building the Options Page (prowp2-settings-api-plugin.zip)</p><p><?php // create custom plugin settings menu add_action( 'admin_menu', 'prowp_create_menu' );</p><p> function prowp_create_menu() {</p><p>//create new top-level menu add_menu_page( 'Halloween Plugin Page', 'Halloween Plugin', 'manage_options', 'prowp_main_menu', 'prowp_settings_page', plugins_url( '/images/wordpress.png', __FILE__ ) );</p><p>//call register settings function add_action( 'admin_init', 'prowp_register_settings' );</p><p>}</p><p> function prowp_register_settings() {</p><p>//register our settings register_setting( 'prowp-settings-group', 'prowp_options', 'prowp_sanitize_options' );</p><p>}</p><p> function prowp_sanitize_options( $input ) {</p><p>$input['option_name'] = sanitize_text_field( $input['option_name'] ); continues</p><p> c08.indd 163 12/6/12 1:19 AM 164 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>LISTING 8-1 (continued)</p><p>$input['option_email'] = sanitize_email( $input['option_email'] ); $input['option_url'] = esc_url( $input['option_url'] );</p><p> return $input;</p><p>}</p><p> function prowp_settings_page() { ?> <div class="wrap"> <h2>Halloween Plugin Options</h2></p><p><form method="post" action="options.php"> <?php settings_fields( 'prowp-settings-group' ); ?> <?php $prowp_options = get_option( 'prowp_options' ); ?> <table class="form-table"> <tr valign="top"> <th scope="row">Name</th> <td><input type="text" name="prowp_options[option_name]" value="<?php echo esc_attr( $prowp_options['option_name'] );?> " /></td> </tr></p><p><tr valign="top"> <th scope="row">Email</th> <td><input type="text" name="prowp_options[option_email]" value="<?php echo esc_attr( $prowp_options['option_email'] ); ?> " /></td> </tr></p><p><tr valign="top"> <th scope="row">URL</th> <td><input type="text" name="prowp_options[option_url]" value="<?php echo esc_url( $prowp_options['option_url'] ); ?>" /> </td> </tr> </table></p><p><p class="submit"> <input type="submit" class="button-primary" value="Save Changes" /> </p></p><p></form> </div> <?php } ?></p><p>The second option page method is to add your plugin settings to an existing Settings page in WordPress, as shown in Figure 8-4. You will also be using the WordPress Settings API functions to hook into these pages and add your plugin settings. </p><p> c08.indd 164 12/6/12 1:19 AM Plugin Settings ❘ 165</p><p>FIGURE 8-4: Custom settings section</p><p>Now look over at the code to create your custom settings section. In the following example, you are going to add a new settings section at the bottom of the Settings ➪ Reading Settings page. This section will contain options for your plugin. <?php //execute our settings section function add_action( 'admin_init', 'prowp_settings_init' );</p><p> function prowp_settings_init() {</p><p>//create the new setting section on the Settings > Reading page add_settings_section( 'prowp_setting_section', 'Halloween Plugin Settings', 'prowp_setting_section', 'reading' );</p><p>// register the individual setting options add_settings_field( 'prowp_setting_enable_id', 'Enable Halloween Feature?', 'prowp_setting_enabled', 'reading', 'prowp_setting_section' );</p><p> add_settings_field( 'prowp_saved_setting_name_id', 'Your Name', 'prowp_setting_name', 'reading', 'prowp_setting_section' );</p><p>// register the setting to store our array of values register_setting( 'reading', 'prowp_setting_values' );</p><p>} ?></p><p> c08.indd 165 12/6/12 1:19 AM 166 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>First, you use the admin_init Action hook to load your custom function prowp_settings_init() before any admin page is rendered. Next, you call the add_settings_section() function to create your new section: <?php add_settings_section( 'prowp_setting_section', 'Halloween Plugin Settings', 'prowp_setting_section', 'reading' ); ?> The fi rst parameter passed is a unique ID for the section. The second parameter is the display name output on the page. Next, you pass in the callback function name to display the actual section itself. The fi nal parameter sets what settings page to add your section to. The accepted default WordPress values are general, writing, reading, discussion, media, privacy, and permalink. <?php // register the individual setting options add_settings_field( 'prowp_setting_enable_id', 'Enable Halloween Feature?', 'prowp_setting_enabled', 'reading', 'prowp_setting_section' );</p><p> add_settings_field( 'prowp_saved_setting_name_id', 'Your Name', 'prowp_setting_name', 'reading', 'prowp_setting_section' ); ?> Now that you’ve registered your custom settings section, you need to register your individual set- ting options. To do this, you’ll be using the add_settings_field() function. The fi rst parameter you are passing is a unique ID for the fi eld. Next, you pass in the title of the field, which is dis- played directly to the left of the option fi eld. The third parameter is the callback function name, which you’ll use to display your option fi eld. The fourth parameter is the settings page where the fi eld should be displayed. The fi nal parameter is the name of the section you are adding the fi eld to, which in this example is the prowp_setting_section you created with the add_setting_ section() function call. <?php register_setting( 'reading', 'prowp_setting_values', 'prowp_sanitize_settings' ); ?> Next, you need to register your setting fi eld. In this example, you are going to register two different settings: one for an enable/disable check box and one for the user’s name. Even though you have two setting fi elds, you are going to store both values in an array, so you only need to register one setting called prowp_setting_values. The fi rst parameter you pass is the option group. In this example, you are saving your options in the reading group with the rest of the reading options. The second parameter is the option name. The option name should be unique and is used to retrieve the value of the option. A third optional parameter can be set for a custom function used to sanitize the option values. In this example, you’ll create a function called prowp_sanitize_settings() to sanitize the option values entered by the user. <?php function prowp_sanitize_settings( $input ) {</p><p>$input['enabled'] = ( $input['enabled'] == 'on' ) ? 'on' : '';</p><p> c08.indd 166 12/6/12 1:19 AM Plugin Settings ❘ 167</p><p>$input['name'] = sanitize_text_field( $input['name'] );</p><p> return $input;</p><p>} ?> As always, you’ll want to sanitize all option values that are entered by the user. The enabled option is a check box, and therefore can only be one of two values: either checked or not. The preceding example uses a PHP ternary operator to determine the value of Enabled. If the check box equals “on,” you know the value is enabled and should save the option value as “on.” If not, the option will save the value as empty, which means the check box is not checked. Now that you’ve registered your setting section, you need to create your custom functions to display it. The fi rst function you’ll create is the prowp_setting_section() that you called in when you created your setting section: <?php function prowp_setting_section() { echo '<p>Configure the Halloween plugin options below</p>'; } ?> This is where you can set the subheading for your settings section. This section is great for plugin instructions, confi guration information, and more. Next, you need to create the function to display your fi rst settings fi eld, Enabled: <?php</p><p> function prowp_setting_enabled() {</p><p>//load plugin options $prowp_options = get_option( 'prowp_setting_values' );</p><p>//display the checkbox form field echo '<input '.checked( $prowp_options['enabled'], 'on', false ).' name="prowp_setting_values[enabled]" type="checkbox" /> Enabled';</p><p>} ?></p><p>This is the callback function you defi ned when you used the add_settings_field() function. The fi rst step is to load the options array if it exists. Because this option is a check box, you know that if it is set, the check box should be checked. In this example, you’ll use the checked() WordPress function. This function has three parameters. The fi rst and second parameters are two values to compare. If the two values are the same, the function will echo checked="checked" thus checking the form element. The third parameter determines whether to echo the value or just return it. In this case, you just want to return it so you set that value to False. Next, you display the actual setting fi eld that will be used in the setting section. Your fi eld input name needs to be the same setting name you registered previously. Because you are saving your options as an array, you need to defi ne the array name value; in this example, it’s prowp_ setting_values[enabled]. This is how the Settings API knows what option to save and where. </p><p> c08.indd 167 12/6/12 1:19 AM 168 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>Your Enabled check box fi eld will display at the bottom of the Settings ➪ Reading page. Now you need to create the function for your second setting fi eld: <?php</p><p> function prowp_setting_name() {</p><p>//load the option value $prowp_options = get_option( 'prowp_setting_values' );</p><p>//display the text form field echo '<input type="text" name="prowp_setting_values[name]" value="'.esc_attr( $prowp_options['name'] ).'" />';</p><p>} ?> As with your check box option, the fi rst thing to do is load the current option value. Then you display your input text fi eld with the same name as defi ned previously in the register_setting() function. As always, be sure to escape the value before displaying in the form fi eld. That’s it! You have successfully created your custom settings section and added it to the Settings ➪ Reading SubPanel. Listing 8-2 shows the full code.</p><p>LISTING 8-2: Custom Settings Section (prowp2-reading-settings-plugin.zip)</p><p><?php</p><p>//execute our settings section function add_action( 'admin_init', 'prowp_settings_init' );</p><p> function prowp_settings_init() {</p><p>//create the new setting section on the Settings > Reading page add_settings_section( 'prowp_setting_section', 'Halloween Plugin Settings', 'prowp_setting_section', 'reading' );</p><p>// register the individual setting options add_settings_field( 'prowp_setting_enable_id', 'Enable Halloween Feature?', 'prowp_setting_enabled', 'reading', 'prowp_setting_section' );</p><p> add_settings_field( 'prowp_saved_setting_name_id', 'Your Name', 'prowp_setting_name', 'reading', 'prowp_setting_section' );</p><p>// register the setting to store our array of values register_setting( 'reading', 'prowp_setting_values', 'prowp_sanitize_settings' );</p><p>}</p><p> function prowp_sanitize_settings( $input ) {</p><p>$input['enabled'] = ( $input['enabled'] == 'on' ) ? 'on' : '';</p><p> c08.indd 168 12/6/12 1:19 AM WordPress Integration ❘ 169</p><p>$input['name'] = sanitize_text_field( $input['name'] );</p><p> return $input;</p><p>}</p><p>// settings section function prowp_setting_section() { echo '<p>Configure the Halloween plugin options below</p>'; }</p><p>// create the enabled checkbox option to save the checkbox value function prowp_setting_enabled() {</p><p>//load plugin options $prowp_options = get_option( 'prowp_setting_values' );</p><p>//display the checkbox form field echo '<input '.checked( $prowp_options['enabled'], 'on', false ) .' name="prowp_setting_values[enabled]" type="checkbox" /> Enabled';</p><p>}</p><p>// create the text field setting to save the name function prowp_setting_name() {</p><p>//load the option value $prowp_options = get_option( 'prowp_setting_values' );</p><p>//display the text form field echo '<input type="text" name="prowp_setting_values[name]" value="'.esc_attr( $prowp_options['name'] ).'" />';</p><p>} ?></p><p>WORDPRESS INTEGRATION</p><p>Integrating your plugin into WordPress is an essential step for users to interact with your plugin in the admin dashboard. WordPress features many different areas where your plugin can be integrated, including a meta box, sidebar, and dashboard widgets, and custom shortcodes.</p><p>Creating a Meta Box WordPress features multiple meta boxes on the Add New Post and Page screens. These meta boxes are used for adding additional information to your posts, pages, and content.</p><p>Meta boxes can be created in a plugin using the add_meta_box() function in WordPress. This function accepts seven parameters, as shown here: <?php add_meta_box( $id, $title, $callback, $page, $context, $priority, $callback_args ); ?></p><p> c08.indd 169 12/6/12 1:19 AM 170 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>Each parameter helps defi ne where and how your meta box is displayed.</p><p>➤ $id: The CSS ID attribute for the meta box ➤ $title: The title displayed in the header of the meta box ➤ $callback: The custom function name to display your meta box information ➤ $page: The page you want your meta box to display on ('post', 'page',, or custom post type name) ➤ $context: The part of the page where the meta box should be displayed ('normal', 'advanced', or 'side') ➤ $priority: The priority within the context where the meta box should display ('high', 'core', 'default', or 'low') ➤ $callback_args: Arguments to pass into your callback function Now that you understand the add_meta_box() function, you can build your fi rst custom meta box in WordPress: <?php add_action( 'add_meta_boxes', 'prowp_meta_box_init' );</p><p>// meta box functions for adding the meta box and saving the data function prowp_meta_box_init() {</p><p>// create our custom meta box add_meta_box( 'prowp-meta', 'Product Information', 'prowp_meta_box', 'post', 'side', 'default' );</p><p>} ?></p><p>The fi rst step to adding your own meta box is to use theadd_meta_boxes Action hook to execute your custom function prowp_meta_box_init(). In this function, you will call the add_meta_box() function to create your custom meta box for Product Information. </p><p>You set the CSS ID attribute to prowp-meta for your meta box. The second parameter is the title, which you set to Product Information. The next parameter is your custom function prowp_meta_ box(), which will display the HTML for your meta box. Next you defi ne your meta box to display on the post page and in the sidebar. Finally, you set the priority to default. Now create your cus- tom prowp_meta_box() function to display your meta box fi elds: function prowp_meta_box( $post, $box ) {</p><p>// retrieve the custom meta box values $prowp_featured = get_post_meta( $post->ID, '_prowp_type', true ); $prowp_price = get_post_meta( $post->ID, '_prowp_price', true );</p><p>//nonce for security wp_nonce_field( plugin_basename( __FILE__ ), 'prowp_save_meta_box' );</p><p>// custom meta box form elements echo '<p>Price: <input type="text" name="prowp_price" </p><p> c08.indd 170 12/6/12 1:19 AM WordPress Integration ❘ 171</p><p> value="'.esc_attr( $prowp_price ).'" size="5" /></p>'; echo '<p>Type: <select name="prowp_product_type" id="prowp_product_type"> <option value="0" ' .selected( $prowp_featured, 'normal', false ). '>Normal </option> <option value="special" ' .selected( $prowp_featured, 'special', false ). '>Special </option> <option value="featured" ' .selected( $prowp_featured, 'featured', false ). '>Featured </option> <option value="clearance" ' .selected( $prowp_featured, 'clearance', false ). '>Clearance </option> </select></p>';</p><p>} The fi rst step in your custom function is to retrieve the saved values for your meta box. If you are creating a new post, there won’t be any saved values yet. Next you display the form elements in your meta box. Notice that you don’t need any <form> tags or a submit button. Also notice that you are using the wp_nonce_field() function to create a custom nonce fi eld in your form. The custom function you just created will generate your custom meta box, as shown in Figure 8-5. Now that you have your meta box and form elements, you need to save that data when your post is saved. To do so, you’ll create a custom function, prowp_save_meta_box(), which is triggered by FIGURE 8-5: Custom meta the save_post Action hook: box <?php // hook to save our meta box data when the post is saved add_action( 'save_post', 'prowp_save_meta_box' );</p><p> function prowp_save_meta_box( $post_id ) {</p><p>// process form data if $_POST is set if( isset( $_POST['prowp_product_type'] ) ) {</p><p>// if auto saving skip saving our meta box data if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;</p><p>//check nonce for security check_admin_referer( plugin_basename( __FILE__ ), 'prowp_save_meta_box' );</p><p>// save the meta box data as post meta using the post ID as a unique prefix update_post_meta( $post_id, '_prowp_type', sanitize_text_field( $_POST['prowp_product_type'] ) ); update_post_meta( $post_id, '_prowp_price', </p><p> c08.indd 171 12/6/12 1:19 AM 172 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p> sanitize_text_field ( $_POST['prowp_price'] ) );</p><p>}</p><p>} ?></p><p>The save_post Action hook runs whenever a post is saved in WordPress. Because you only want to work with the custom metadata in the meta box, the fi rst thing you’ll do is verify that the $_POST['prowp_product_type'] value is set. Next, you need to verify that the post being saved is an active post and not an auto save. To do so, you check that the post is not auto-saving and, if so, you exit the function. The next step is to verify that the nonce value is the expected value. If the post is active and your form elements have been set, you save the form data. Once all checks have passed, you use update_post_meta() to save your meta box data as metadata against your post.</p><p>As you can see, you send in the post ID as the fi rst parameter to update_post_meta(). This tells WordPress what post the meta data will be attached to. Next, you pass in the name of the meta key you are updating. Notice the meta key name is prefi xed with an underscore. This prevents these values from being listed in the custom fi elds meta box on the post edit screen. Because you’ve provided a UI to edit these values, you don’t need them in the custom fi elds box. The fi nal parameter you send is the new value for the meta key, which is being sanitized using the sanitize_text_field() WordPress function. You now have a fully functional custom meta box that saves individual data against each post. Listing 8-3 shows the full custom meta box code.</p><p>LISTING 8-3: Custom Meta Box (prowp2-custom-meta-box.zip)</p><p><?php add_action( 'add_meta_boxes', 'prowp_meta_box_init' );</p><p>// meta box functions for adding the meta box and saving the data function prowp_meta_box_init() {</p><p>// create our custom meta box add_meta_box( 'prowp-meta', 'Product Information', 'prowp_meta_box', 'post', 'side', 'default' );</p><p>}</p><p> function prowp_meta_box( $post, $box ) {</p><p>// retrieve the custom meta box values $prowp_featured = get_post_meta( $post->ID, '_prowp_type', true ); $prowp_price = get_post_meta( $post->ID, '_prowp_price', true );</p><p>//nonce for security wp_nonce_field( plugin_basename( __FILE__ ), 'prowp_save_meta_box' );</p><p>// custom meta box form elements echo '<p>Price: <input type="text" name="prowp_price" value="'.esc_attr( $prowp_price ).'" size="5" /></p>';</p><p> c08.indd 172 12/6/12 1:19 AM WordPress Integration ❘ 173</p><p> echo '<p>Type: <select name="prowp_product_type" id="prowp_product_type"> <option value="0" ' .selected( $prowp_featured, 'normal', false ) . '>Normal</option> <option value="special" ' .selected( $prowp_featured, 'special', false ) . '>Special</option> <option value="featured" ' .selected( $prowp_featured, 'featured', false ) . '>Featured</option> <option value="clearance" ' .selected( $prowp_featured, 'clearance', false ) . '>Clearance</option> </select></p>';</p><p>}</p><p>// hook to save our meta box data when the post is saved add_action( 'save_post', 'prowp_save_meta_box' );</p><p> function prowp_save_meta_box( $post_id ) {</p><p>// process form data if $_POST is set if( isset( $_POST['prowp_product_type'] ) ) {</p><p>// if auto saving skip saving our meta box data if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;</p><p>//check nonce for security check_admin_referer( plugin_basename( __FILE__ ), 'prowp_save_meta_box' );</p><p>// save the meta box data as post meta using the post ID as a unique prefix update_post_meta( $post_id, '_prowp_type', sanitize_text_field( $_POST['prowp_product_type'] ) ); update_post_meta( $post_id, '_prowp_price', sanitize_text_field( $_POST['prowp_price'] ) );</p><p>}</p><p>} ?> Now that you’ve saved your meta box data, you’ll probably want to display it somewhere. You can easily display your saved meta box data in your theme using the get_post_meta function inside the Loop like so: <?php $prowp_type = get_post_meta( $post->ID, '_prowp_type', true ); $prowp_price = get_post_meta( $post->ID, '_prowp_price', true );</p><p> echo '<p>Price: ' .esc_html( $prowp_price ). '</p>'; echo '<p>Type: ' .esc_html( $prowp_type ). '</p>'; ?></p><p> c08.indd 173 12/6/12 1:19 AM 174 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>Adding a custom meta box is a great way to extend the data on posts and pages and is very intuitive for users as well.</p><p>Shortcodes WordPress features a Shortcode API that can be used to easily create shortcode functionality in your plugins. Shortcodes are basically text macro codes that can be inserted into a post, page, or custom post type. When being displayed, these shortcodes are replaced by some other type of content. Look at a simple example using the Shortcode API: <?php add_shortcode( 'mytwitter', 'prowp_twitter' );</p><p> function prowp_twitter() {</p><p> return '<a href="http://twitter.com/williamsba">@williamsba</a>';</p><p>} ?></p><p>Now any time you use the [mytwitter] shortcode in your content, it will be replaced with an HTML link to my Twitter account when displayed in the browser. As you can see, this is a very powerful feature in WordPress, which many plugins out there currently take advantage of, often inserting small pieces of JavaScript to place a button or advertisement in the specifi c spot in a post. Shortcodes can also be confi gured to accept attributes. This is very useful for passing arguments to your custom functions, thereby altering the output of the shortcode based on those arguments. Modify your shortcode function to accept a site parameter: <?php add_shortcode( 'mytwitter', 'prowp_twitter' );</p><p> function prowp_twitter( $atts, $content = null ) {</p><p> extract( shortcode_atts( array( 'person' => 'brad' // set attribute default ), $atts ) );</p><p> if ( $person == 'brad' ) { return '<a href="http://twitter.com/williamsba">@williamsba</a>'; }elseif ( $person == 'david' ) { return '<a href="http://twitter.com/mirmillo">@mirmillo</a>'; }elseif ( $person == 'hal' ) { return '<a href="http://twitter.com/freeholdhal">@freeholdhal</a>'; } } ?> This code creates the same shortcode as before, but now you are defi ning an attribute called person. With this attribute, you can specify which person you want to display a Twitter link for. To display the Twitter URL for David, you would use the shortcode[mytwitter person="david"]. Alternatively, you can also easily display the Twitter URL for Hal like so: [mytwitter person="hal"]. Shortcodes can also accept multiple attributes from the array set in your shortcode function.</p><p> c08.indd 174 12/6/12 1:19 AM WordPress Integration ❘ 175</p><p>Creating a Widget Widgets are a common feature included in many WordPress plugins. By creating a widget with your plugin, you can easily give users a way to add your plugin information to their sidebar or other widgetized areas.</p><p>To understand how widgets work, it’s helpful to view an overview of the WP_Widget class in WordPress. The widget class features built-in functions for building a widget, each with a specifi c purpose, as shown in the following code: <?php class My_Widget extends WP_Widget { function My_Widget() { // process the widget }</p><p> function form($instance) { // widget form in admin dashboard }</p><p> function update($new_instance, $old_instance) { // save widget options }</p><p> function widget($args, $instance) { // display the widget } } ?> As an example, you’ll create a basic bio widget. This widget will allow you to set a person’s name and custom bio to display in a widgetized sidebar in WordPress. The fi rst step in creating your own widget is to use the appropriate hook to initialize your widget. This hook is called widgets_init and is triggered right after the default WordPress widgets have been registered: add_action( 'widgets_init', 'prowp_register_widgets' );</p><p> function prowp_register_widgets() {</p><p> register_widget( 'prowp_widget' );</p><p>}</p><p>Calling the Action hook widgets_init executes the function prowp_register_widgets(), as shown in the preceding code. Here you register your widget called pro_widget. You could also register multiple widgets in this function if needed. The revamped Widget API released with WordPress 2.8 makes creating a widget much easier than before. To begin, you have to extend the preexisting WP_Widget class by creating a new class with a unique name, as shown here: class prowp_widget extends WP_Widget {</p><p> c08.indd 175 12/6/12 1:19 AM 176 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>Next, you’ll add your fi rst function, which should be the same name as your unique class name. This is referred to as the constructor: function prowp_widget() {</p><p>$widget_ops = array( 'classname' => 'prowp_widget_class', 'description' => 'Example widget that displays a user\'s bio.' ); $this->WP_Widget( 'prowp_widget', 'Bio Widget', $widget_ops );</p><p>}</p><p>In your prowp_widget() function, you defi ne your classname for your widget. The classname is the CSS class that will be added to the HTML tag wrapping the widget when it’s displayed. Depending on the theme the CSS class may be in a <div>, <aside>, <li>, or other HTML tag. You also set the description for your widget. This is displayed on the widget dashboard below the widget name. These options are then passed to WP_Widget. You also pass the CSS ID name (prowp_widget_ class) and the widget name (Bio Widget). Next you need to create the function to build your widget settings form. Widget settings are located on the widget admin page upon expanding any widget listed on a sidebar. The widget class makes this process very easy, as shown in the following code: function form( $instance ) { $defaults = array( 'title' => 'My Bio', 'name' => 'Michael Myers', 'bio' => '' ); $instance = wp_parse_args( (array) $instance, $defaults ); $title = $instance['title']; $name = $instance['name']; $bio = $instance['bio']; ?> <p>Title: <input class="widefat" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" /></p> <p>Name: <input class="widefat" name="<?php echo $this->get_field_name( 'name' ); ?>" type="text" value="<?php echo esc_attr( $name ); ?>" /></p> <p>Bio: <textarea class="widefat" name="<?php echo $this->get_field_name( 'bio' ); ?>" > <?php echo esc_textarea( $bio ); ?></textarea></p> <?php } The fi rst thing you do is defi ne your default widget values. If the user doesn’t fi ll in the settings, you can default these values to whatever you like. In this case, you’re setting the default title to My Bio and default name to Michael Myers. Next, you pull in the instance values, which are your widget settings. If the widget was just added to a sidebar, there are no settings saved so these values will be empty. Finally, you display the three form fi elds for your widget settings: title, name, and bio. The </p><p> c08.indd 176 12/6/12 1:19 AM WordPress Integration ❘ 177</p><p> fi rst two values are using text input boxes and the bio value is using a text area box. Notice that you don’t need <form> tags or a submit button; the widget class will handle this for you. Remember to use the appropriate escaping functions when displaying your data, in this case esc_attr() for the two text fi elds and esc_textarea() for the text area fi eld. Next, you need to save your widget settings using the update() widget class function: function update( $new_instance, $old_instance ) {</p><p>$instance = $old_instance; $instance['title'] = sanitize_text_field( $new_instance['title'] ); $instance['name'] = sanitize_text_field( $new_instance['name'] ); $instance['bio'] = sanitize_text_field( $new_instance['bio'] );</p><p> return $instance;</p><p>} This function is pretty straightforward. You’ll notice you don’t need to save the settings yourself, the widget class does it for you. You pass in the $new_instance values for each of your setting fi elds. You’re also using sanitize_text_field() to strip out any HTML that might be entered. If you want to accept HTML values, you’d use wp_kses() instead, which was covered earlier in the, “Data Validation and Sanitization,” section of this chapter.</p><p>The fi nal function in your prowp_widget class handles displaying your widget: function widget( $args, $instance ) { extract( $args );</p><p> echo $before_widget;</p><p>$title = apply_filters( 'widget_title', $instance['title'] ); $name = ( empty( $instance['name'] ) ) ? ' ' : $instance['name']; $bio = ( empty( $instance['bio'] ) ) ? ' ' : $instance['bio'];</p><p> if ( !empty( $title ) ) { echo $before_title . esc_html( $title ) . $after_title; }; echo '<p>Name: ' . esc_html( $name ) . '</p>'; echo '<p>Bio: ' . esc_html( $bio ) . '</p>';</p><p> echo $after_widget;</p><p>}</p><p>The fi rst thing you do is extract the $args parameter. This variable stores some global theme values such as $before_widget and $after_widget. These variables can be used by theme developers to customize what code will wrap your widget — for example, a custom <div> tag. After extracting the $args parameter, you display the $before_widget variable. The $before_title and $after_ title are also set in this variable. This is useful for passing custom HTML tags to wrap the widget title in. </p><p>Next, you display your widget values. The title is displayed fi rst and wrapped by $before_title and $after_title. Next, you echo out the name and bio values. Remember to escape the widget values for security reasons. Finally, you display the $after_widget value.</p><p> c08.indd 177 12/6/12 1:19 AM 178 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>That’s it! You’ve just created a custom widget for your plugin using the widget class in WordPress. Remember that by using the new widget class, you can add multiple copies of the same widget to the sidebar or additional sidebars. Listing 8-4 shows the completed widget code.</p><p>LISTING 8-4: Custom Widget (prowp2-custom-widget.zip)</p><p><?php</p><p>// use widgets_init Action hook to execute custom function add_action( 'widgets_init', 'prowp_register_widgets' );</p><p>//register our widget function prowp_register_widgets() {</p><p> register_widget( 'prowp_widget' );</p><p>}</p><p>//prowpwidget class class prowp_widget extends WP_Widget {</p><p>//process our new widget function prowp_widget() {</p><p>$widget_ops = array( 'classname' => 'prowp_widget_class', 'description' => 'Example widget that displays a user\'s bio.' ); $this->WP_Widget( 'prowp_widget', 'Bio Widget', $widget_ops );</p><p>}</p><p>//build our widget settings form function form( $instance ) { $defaults = array( 'title' => 'My Bio', 'name' => 'Michael Myers', 'bio' => '' ); $instance = wp_parse_args( (array) $instance, $defaults ); $title = $instance['title']; $name = $instance['name']; $bio = $instance['bio']; ?> <p>Title: <input class="widefat" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" /></p> <p>Name: <input class="widefat" name="<?php echo $this->get_field_name( 'name' ); </p><p> c08.indd 178 12/6/12 1:19 AM WordPress Integration ❘ 179</p><p>?>" type="text" value="<?php echo esc_attr( $name ); ?>" /></p> <p>Bio: <textarea class="widefat" name="<?php echo $this->get_field_name( 'bio' ); ?>" ><?php echo esc_textarea( $bio ); ?></textarea></p> <?php }</p><p>//save our widget settings function update( $new_instance, $old_instance ) {</p><p>$instance = $old_instance; $instance['title'] = sanitize_text_field( $new_instance['title'] ); $instance['name'] = sanitize_text_field( $new_instance['name'] ); $instance['bio'] = sanitize_text_field( $new_instance['bio'] );</p><p> return $instance;</p><p>}</p><p>//display our widget function widget( $args, $instance ) { extract( $args );</p><p> echo $before_widget;</p><p>$title = apply_filters( 'widget_title', $instance['title'] ); $name = ( empty( $instance['name'] ) ) ? ' ' : $instance['name']; $bio = ( empty( $instance['bio'] ) ) ? ' ' : $instance['bio'];</p><p> if ( !empty( $title ) ) { echo $before_title . esc_html( $title ) . $after_title; }; echo '<p>Name: ' . esc_html( $name ) . '</p>'; echo '<p>Bio: ' . esc_html( $bio ) . '</p>';</p><p> echo $after_widget;</p><p>} } ?> Creating a Dashboard Widget WordPress 2.7 introduced Dashboard Widgets, which are the widgets displayed on the main Dashboard of your WordPress installation. Along with these new widgets came the Dashboard Widgets API, which allows you to create any custom Dashboard Widget that you would like.</p><p> c08.indd 179 12/6/12 1:19 AM 180 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>To create a custom Dashboard Widget, you’ll use the wp_add_dashboard_widget() function, as shown here: <?php add_action( 'wp_dashboard_setup', 'prowp_add_dashboard_widget' );</p><p>// call function to create our dashboard widget function prowp_add_dashboard_widget() {</p><p> wp_add_dashboard_widget( 'prowp_dashboard_widget', 'Pro WP Dashboard Widget', 'prowp_create_dashboard_widget' );</p><p>}</p><p>// function to display our dashboard widget content function prowp_create_dashboard_widget() {</p><p> echo '<p>Hello World! This is my Dashboard Widget</p>';</p><p>} ?></p><p>First you call the wp_dashboard_setup Action hook to execute the function to build your custom Dashboard Widget. This hook is triggered after all of the default Dashboard Widgets have been built. Next you execute the wp_add_dashboard_widget() function to create your Dashboard Widget. The fi rst parameter is the widget ID slug. This is used for the CSS classname and the key in the array of widgets. The next parameter is the display name for your widget. The fi nal parameter you send is your custom function name to display your widget contents. An optional fourth parameter can be sent for a control callback function. This function would be used to process any form elements that might exist in your Dashboard Widget.</p><p>After executing the wp_add_dashboard_widget() function your custom function is called to display your widget contents. In this example, you display a simple string. The result is a custom Dashboard Widget, as shown in Figure 8-6.</p><p>Creating Custom Tables FIGURE 8-6: Example dashboard widget WordPress contains a variety of tables in which to store your plugin data. However, you might fi nd that your plugin needs a custom table or two to store plugin data. This can be useful for more complex plugins such as an e-commerce plugin, which stores order history, product and inventory data, and other data that is accessed using database SQL semantics rather than the simple key and value pairing of the options table. The fi rst step in creating a custom database table is to create an installation function. You will execute this function when the plugin is activated to create your new table.</p><p> c08.indd 180 12/6/12 1:19 AM WordPress Integration ❘ 181</p><p><?php register_activation_hook( __FILE__, 'prowp_install' );</p><p> function prowp_install() {</p><p>} ?> Now that you have an installation function, you need to defi ne your custom table name. Remember that the table prefi x can be custom defi ned by the user in wp-config.php, and as discussed in Chapter 10, WordPress Multisite can insert additional prefi x data into the table names so you need to incorporate these table prefi xes for your custom table name. To get the table prefi x, you use the global $wpdb->prefix value like so: global $wpdb;</p><p>//define the custom table name $table_name = $wpdb->prefix .'prowp_data';</p><p>This code stores your table named wp_prowp_data in the $table_name variable, assuming your WordPress table prefi x is set towp_ . Now it’s time to build your SQL query for creating your new table. You’ll create your query in a variable called $sql before executing it. You also need to include the upgrade.php fi le prior to executing your query like so: $sql = "CREATE TABLE " .$table_name ." ( id mediumint(9) NOT NULL AUTO_INCREMENT, time bigint(11) DEFAULT '0' NOT NULL, name tinytext NOT NULL, text text NOT NULL, url VARCHAR(55) NOT NULL, UNIQUE KEY id (id) );"; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );</p><p>//execute the query creating our table</p><p> dbDelta( $sql );</p><p>After this executes, your new table has been created in the database. The dbDelta() function will verify fi rst that the table you are creating doesn’t exist, so you don’t have to worry about checking if a table exists before creating it. It’s also a good idea to save the version number for your database table structure. This can help down the road if you upgrade your plugin and need to change the table structure. You can check what table version the users have installed for your plugin and determine if they need to upgrade: $prowp_db_version = '1.0';</p><p> add_option( 'prowp_db_version', $prowp_db_version ); Look at the full function in action: register_activation_hook( __FILE__, 'prowp_install' );</p><p> function prowp_install() {</p><p> c08.indd 181 12/6/12 1:19 AM 182 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p> global $wpdb;</p><p>//define the custom table name $table_name = $wpdb->prefix .'prowp_data';</p><p>//build the query to create our new table $sql = "CREATE TABLE " .$table_name ." ( id mediumint(9) NOT NULL AUTO_INCREMENT, time bigint(11) DEFAULT '0' NOT NULL, name tinytext NOT NULL, text text NOT NULL, url VARCHAR(55) NOT NULL, UNIQUE KEY id (id) );";</p><p> require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );</p><p>//execute the query to create our table dbDelta( $sql );</p><p>//set the table structure version $prowp_db_version = '1.0';</p><p>//save the table structure version number add_option( 'prowp_db_version', $prowp_db_version );</p><p>} If you want to upgrade your table structure for a new version of your plugin, you can just compare the table structure version numbers: $installed_ver = get_option( 'gmp_db_version' );</p><p> if( $installed_ver != $prowp_db_version ) {</p><p>//update database table here</p><p>//update table version update_option( 'gmp_db_version', $prowp_db_version );</p><p>} Before creating a custom table for your plugin, you should consider whether this is the best method. It’s generally a good idea to avoid creating custom tables unless there is no alternative. Remember that you can easily store options in WordPress using the options API. You can also utilize the wp_*meta tables for storing extended data about posts, pages, comments, and users. Custom post types are also a great place to store data. To work with a custom table once you’ve created it, you’ll need to use the WordPress database class, as shown in Chapter 6.</p><p>Uninstalling Your Plugin A nice feature to include with your plugin is an uninstall feature. WordPress features two ways to register the uninstaller for your plugin: the uninstall.php method and the uninstall hook. Both methods are executed when a deactivated plugin is deleted in the WordPress admin dashboard.</p><p> c08.indd 182 12/6/12 1:19 AM WordPress Integration ❘ 183</p><p>The fi rst method you’ll look at is the uninstall.php uninstaller method. This is the preferred method for uninstalling a plugin. The fi rst step to using this method is to create an uninstall.php fi le. This fi le must exist in the root directory of your plugin, and if it does, it will execute in preference to the uninstall hook. <?php // If uninstall/delete not called from WordPress then exit if( !defined( 'ABSPATH' ) && !defined( 'WP_UNINSTALL_PLUGIN' ) ) exit();</p><p>// Delete option from options table delete_option( 'prowp_options_arr' );</p><p>// Delete any other options, custom tables/data, files ?></p><p>The fi rst thing youruninstall.php fi le should check is that ABSPATH and WP_UNINSTALL_PLUGIN constants have been defi ned, meaning they were actually called from WordPress. This is a security measure to ensure this fi le is not executed except during the uninstall process of your plugin. The next step is to remove any options and custom tables your plugin created. In a perfect uninstall scenario there would be no trace of your plugin left over in the database once it had been uninstalled. The preceding example uses delete_option() to delete the option array. Remember that once this function runs, all custom plugin data saved will be destroyed. The second method for uninstalling a plugin is to use the Uninstall hook. When a plugin is deleted, and uninstall.php does not exist but the Uninstall hook does exist, the plugin will be run one last time to execute the Uninstall hook. After the hook has been called, your plugin will be deleted. Here’s the Uninstall hook in action: <?php register_uninstall_hook( __FILE__, 'prowp_uninstall_hook' );</p><p> function prowp_uninstall_hook() {</p><p> delete_option( 'prowp_options_arr' );</p><p>//remove any additional options and custom tables</p><p>} ?> First you call your custom uninstall function to properly uninstall your plugin options. If you do include uninstall functionality in your plugin, such as removing custom tables and options, make sure to warn the users that all plugin data will be deleted if they delete the plugin.</p><p>The difference between this method and the register_deactivation_hook is that the register_uninstall_hook is executed when a deactivated plugin is deleted. The register _deactivation_hook is executed when the plugin is deactivated, which means the user may want to activate the plugin again eventually. You wouldn’t want to delete all of the plugin settings if the user is planning on using your plugin again.</p><p> c08.indd 183 12/6/12 1:19 AM 184 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>CREATING A PLUGIN EXAMPLE</p><p>Now that you’ve seen the many different options WordPress provides for use in your plugins, you can put that knowledge to work! In this example, you will utilize many of the features covered in this chapter. At the end of this section, the entire plugin source code will be available. The example plugin you are going to build is a basic Halloween Store. The goal of this plugin is to create an easy way to add products to WordPress and display the products in your Halloween Store. This plugin will include the following features: ➤ Settings page using the Settings API ➤ Widget for displaying newest products using the Widget class ➤ Post meta box for adding product metadata ➤ Shortcode support to easily display product data in a post ➤ Internationalization support using translation functions The fi rst step in creating your plugin is to create your plugin fi les. For this plugin, you’ll have two fi les: halloween-store.php and uninstall.php. Because your plugin contains two fi les, you’ll need to save these fi les in a separate folder for your plugin named halloween-store. Next, you need to set up your plugin header and license.</p><p>To start, you’ll be working in halloween-store.php. First you want to defi ne your plugin header, as shown here: <?php /* Plugin Name: Halloween Store Plugin URI: http://webdevstudios.com/support/wordpress-plugins/ Description: Create a Halloween Store to display product information Version: 1.0 Author: Brad Williams Author URI: http://webdevstudios.com License: GPLv2 */</p><p>/* Copyright 2013 Brad Williams (email : brad@webdevstudios.com)</p><p>This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</p><p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</p><p>You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */</p><p> c08.indd 184 12/6/12 1:19 AM Creating a Plugin Example ❘ 185</p><p>As you can see, you created the appropriate plugin header for your new plugin. Because you will be releasing this plugin, you’ll want to include the GPL software license below your plugin header.</p><p>Next you are going to call the register_activation_hook() function to set up your default plugin settings. Remember that this function is triggered when a user activates your plugin in WordPress. // Call function when plugin is activated register_activation_hook( __FILE__, 'halloween_store_install' );</p><p> function halloween_store_install() {</p><p>//setup default option values $hween_options_arr = array( 'currency_sign' => '$' );</p><p>//save our default option values update_option( 'halloween_options', $hween_options_arr );</p><p>}</p><p>As you can see, this plugin will store an array of settings in a single option called halloween_ options. When the plugin is activated, you set the default currency_sign value to $.</p><p>Next, you call the init hook to register the custom post type for Products. This is how you will add and manage your Halloween Store products. // Action hook to initialize the plugin add_action( 'init', 'halloween_store_init' );</p><p>//Initialize the Halloween Store function halloween_store_init() {</p><p>//register the products custom post type $labels = array( 'name' => __( 'Products', 'halloween-plugin' ), 'singular_name' => __( 'Product', 'halloween-plugin' ), 'add_new' => __( 'Add New', 'halloween-plugin' ), 'add_new_item' => __( 'Add New Product', 'halloween-plugin' ), 'edit_item' => __( 'Edit Product', 'halloween-plugin' ), 'new_item' => __( 'New Product', 'halloween-plugin' ), 'all_items' => __( 'All Products', 'halloween-plugin' ), 'view_item' => __( 'View Product', 'halloween-plugin' ), 'search_items' => __( 'Search Products', 'halloween-plugin' ), 'not_found' => __( 'No products found', 'halloween-plugin' ), 'not_found_in_trash' => __( 'No products found in Trash', 'halloween-plugin' ), 'menu_name' => __( 'Products', 'halloween-plugin' ) );</p><p>$args = array( 'labels' => $labels, 'public' => true, 'publicly_queryable' => true, 'show_ui' => true, </p><p> c08.indd 185 12/6/12 1:19 AM 186 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>'show_in_menu' => true, 'query_var' => true, 'rewrite' => true, 'capability_type' => 'post', 'has_archive' => true, 'hierarchical' => false, 'menu_position' => null, 'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ) ); </p><p> register_post_type( 'halloween-products', $args );</p><p>}</p><p>Notice that you are wrapping each translatable term in the __() translation function. This allows users to translate the terms into any language they want. You’ll see these translation functions used throughout this plugin example. Now you’ll create the Halloween Store settings page. The fi rst step is to add a Settings submenu item for your settings page using the add_options_page() function: // Action hook to add the post products menu item add_action( 'admin_menu', 'halloween_store_menu' );</p><p>//create the Halloween Masks sub-menu function halloween_store_menu() {</p><p> add_options_page( __( 'Halloween Store Settings Page', 'halloween-plugin' ), __( 'Halloween Store Settings', 'halloween-plugin' ), 'manage_options', 'halloween-store-settings', 'halloween_store_settings_page' );</p><p>} As you can see, this function is used to create your submenu item. Your Halloween Store Settings submenu item will be located at the bottom of the Settings menu in your Dashboard. You also set this menu item to be viewable by an administrator only. Now you need to build the actual settings page. As shown in the preceding code, the Halloween Store Settings page triggers your custom halloween_store_settings_page() function. //build the plugin settings page function halloween_store_settings_page() {</p><p>//load the plugin options array $hween_options_arr = get_option( 'halloween_options' );</p><p>//set the option array values to variables $hs_inventory = ( ! empty( $hween_options_arr['show_inventory'] ) ) ? $hween_options_arr['show_inventory'] : ''; $hs_currency_sign = $hween_options_arr['currency_sign']; ?> <div class="wrap"> <h2><?php _e( 'Halloween Store Options', 'halloween-plugin' ) ?></h2></p><p><form method="post" action="options.php"></p><p> c08.indd 186 12/6/12 1:19 AM Creating a Plugin Example ❘ 187</p><p><?php settings_fields( 'halloween-settings-group' ); ?> <table class="form-table"> <tr valign="top"> <th scope="row"><?php _e( 'Show Product Inventory', 'halloween-plugin' ) ?></th> <td><input type="checkbox" name="halloween_options[show_inventory]" <?php echo checked( $hs_inventory, 'on' ); ?> /></td> </tr></p><p><tr valign="top"> <th scope="row"><?php _e( 'Currency Sign', 'halloween-plugin' ) ?></th> <td><input type="text" name="halloween_options[currency_sign]" value="<?php echo esc_attr( $hs_currency_sign ); ?>" size="1" maxlength="1" /></td> </tr> </table></p><p><p class="submit"> <input type="submit" class="button-primary" value="<?php _e( 'Save Changes', 'halloween-plugin' ); ?>" /> </p></p><p></form> </div> <?php } Your Halloween Store plugin has two options: whether to show product inventory and the currency sign to use. First you load your plugin options array value. Next, set the two option values to variables. You use a PHP ternary operator to set the default value for Inventory. You also load in the current currency value into a variable for display. Next, you display your settings page form with both option form fi elds listed. Notice that you are using the settings_fields() function to link your settings form to your registered setting that you will defi ne in the code that follows. This is the proper way to save your setting options in an array using the Settings API. When the form is submitted, WordPress will use the Settings API to sanitize the form values and save them in the database. To make this work, you need to register your settings fi eld and sanitiza- tion functions: // Action hook to register the plugin option settings add_action( 'admin_init', 'halloween_store_register_settings' );</p><p> function halloween_store_register_settings() {</p><p>//register the array of settings register_setting( 'halloween-settings-group', 'halloween_options', 'halloween_sanitize_options' );</p><p>}</p><p> function halloween_sanitize_options( $options ) {</p><p>$options['show_inventory'] = ( ! empty( $options['show_inventory'] ) ) ? sanitize_text_field( $options['show_inventory'] ) : ''; $options['currency_sign'] = ( ! empty( $options['currency_sign'] ) ) ?</p><p> c08.indd 187 12/6/12 1:19 AM 188 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p> sanitize_text_field( $options['currency_sign'] ) : '';</p><p> return $options;</p><p>}</p><p>Using the register_setting() function, you register the settings group, halloween-settings- group, and the option name, halloween-options, to be used in your settings form. The hallow- een_sanitize_options() function is used to sanitize the user input for each setting prior to saving in WordPress. This is a very important security step to verify that the data being submitted is prop- erly sanitized before being saved in the database. Now that your plugin settings are saved, it’s time to register the Meta Box for saving Product metadata: //Action hook to register the Products meta box add_action( 'add_meta_boxes', 'halloween_store_register_meta_box' );</p><p> function halloween_store_register_meta_box() {</p><p>// create our custom meta box add_meta_box( 'halloween-product-meta', __( 'Product Information','halloween-plugin' ), 'halloween_meta_box', 'halloween-products', 'side', 'default' );</p><p>}</p><p>Using the add_meta_boxes action hook, you’ll call your custom function for registering the Products meta box. The add_meta_box() function is used to do the actual registering. Now that the meta box is registered, you need to build the meta box form: //build product meta box function halloween_meta_box( $post ) {</p><p>// retrieve our custom meta box values $hween_sku = get_post_meta( $post->ID, '_halloween_product_sku', true ); $hween_price = get_post_meta( $post->ID, '_halloween_product_price', true ); $hween_weight = get_post_meta( $post->ID, '_halloween_product_weight', true ); $hween_color = get_post_meta( $post->ID, '_halloween_product_color', true ); $hween_inventory = get_post_meta( $post->ID, '_halloween_product_inventory', true );</p><p>//nonce field for security wp_nonce_field( 'meta-box-save', 'halloween-plugin' );</p><p>// display meta box form echo '<table>'; echo '<tr>'; echo '<td>' .__('Sku', 'halloween-plugin').':</td> <td><input type="text" name="halloween_product_sku" value="'.esc_attr( $hween_sku ).'" size="10"></td>'; echo '</tr><tr>'; echo '<td>' .__('Price', 'halloween-plugin').':</td> <td><input type="text" name="halloween_product_price" value="'.esc_attr( $hween_price ).'" size="5"></td>'; echo '</tr><tr>';</p><p> c08.indd 188 12/6/12 1:19 AM Creating a Plugin Example ❘ 189</p><p> echo '<td>' .__('Weight', 'halloween-plugin').':</td> <td><input type="text" name="halloween_product_weight" value="'.esc_attr( $hween_weight ).'" size="5"></td>'; echo '</tr><tr>'; echo '<td>' .__('Color', 'halloween-plugin').':</td> <td><input type="text" name="halloween_product_color" value="'.esc_attr( $hween_color ).'" size="5"></td>'; echo '</tr><tr>'; echo '<td>Inventory:</td> <td><select name="halloween_product_inventory" id="halloween_product_inventory"> <option value="In Stock"' .selected( $hween_inventory, 'In Stock', false ). '>' .__( 'In Stock', 'halloween-plugin' ). '</option> <option value="Backordered"' .selected( $hween_inventory, 'Backordered', false ). '>' .__( 'Backordered', 'halloween-plugin' ). '</option> <option value="Out of Stock"' .selected( $hween_inventory, 'Out of Stock', false ). '>' .__( 'Out of Stock', 'halloween-plugin' ). '</option> <option value="Discontinued"' .selected( $hween_inventory, 'Discontinued', false ). '>' .__( 'Discontinued', 'halloween-plugin' ). '</option> </select></td>'; echo '</tr>';</p><p>//display the meta box shortcode legend section echo '<tr><td colspan="2"><hr></td></tr>'; echo '<tr><td colspan="2"><strong>' .__( 'Shortcode Legend', 'halloween-plugin' ).'</strong></td></tr>'; echo '<tr><td>' .__( 'Sku', 'halloween-plugin' ) .': </td><td>[hs show=sku]</td></tr>'; echo '<tr><td>' .__( 'Price', 'halloween-plugin' ).': </td><td>[hs show=price]</td></tr>'; echo '<tr><td>' .__( 'Weight', 'halloween-plugin' ).': </td><td>[hs show=weight]</td></tr>'; echo '<tr><td>' .__( 'Color', 'halloween-plugin' ).': </td><td>[hs show=color]</td></tr>'; echo '<tr><td>' .__( 'Inventory', 'halloween-plugin' ).': </td><td>[hs show=inventory]</td></tr>'; echo '</table>'; } Your Halloween Store plugin saves fi ve different product values on every product: SKU, price, weight, color, and inventory. As you can see, the fi rst step is to load these five custom fi eld values. Next, you display the meta box form and fi ll in the current values if any exist. Below the meta box form, you display a simple shortcode legend to show the user what shortcode options are available for displaying the product metadata. Once completed, your custom meta box will look like Figure 8-7.</p><p>FIGURE 8-7: Post product meta box</p><p> c08.indd 189 12/6/12 1:19 AM 190 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>Now that you’ve created your custom meta box, you need to save the data entered in the form, as shown in the following code: // Action hook to save the meta box data when the post is saved add_action( 'save_post','halloween_store_save_meta_box' );</p><p>//save meta box data function halloween_store_save_meta_box( $post_id ) {</p><p>//verify the post type is for Halloween Products and metadata has been posted if ( get_post_type( $post_id ) == 'halloween-products' && isset( $_POST['halloween_product_sku'] ) ) {</p><p>//if autosave skip saving data if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;</p><p>//check nonce for security check_admin_referer( 'meta-box-save', 'halloween-plugin' );</p><p>// save the meta box data as post metadata update_post_meta( $post_id, '_halloween_product_sku', sanitize_text_field( $_POST['halloween_product_sku'] ) ); update_post_meta( $post_id, '_halloween_product_price', sanitize_text_field( $_POST['halloween_product_price'] ) ); update_post_meta( $post_id, '_halloween_product_weight', sanitize_text_field( $_POST['halloween_product_weight'] ) ); update_post_meta( $post_id, '_halloween_product_color', sanitize_text_field( $_POST['halloween_product_color'] ) ); update_post_meta( $post_id, '_halloween_product_inventory', sanitize_text_field( $_POST['halloween_product_inventory'] ) );</p><p>}</p><p>}</p><p>First you need to verify that the post being saved is a halloween-products custom post type entry. You also verify the $_POST['halloween_product_sku'] value is set before proceeding. The only required fi eld is the product SKU, so if this fi eld is blank, the product data will not be saved. After you have verifi ed that a SKU exists, you need to verify that the post is not an autosave. You also need to verify the nonce for security using check_admin_referer(). After all checks have passed, you save your custom product fi elds as product metadata for the product you are creating or updating. Next, you’re going to set up the plugin shortcode. This will allow you to easily display any or all Product metadata in the Product content.</p><p>// Action hook to create the products shortcode add_shortcode( 'hs', 'halloween_store_shortcode' );</p><p>//create shortcode function halloween_store_shortcode( $atts, $content = null ) {</p><p> c08.indd 190 12/6/12 1:20 AM Creating a Plugin Example ❘ 191</p><p> global $post;</p><p> extract( shortcode_atts( array( "show" => '' ), $atts ) );</p><p>//load options array $hween_options_arr = get_option( 'halloween_options' );</p><p> if ( $show == 'sku') {</p><p>$hs_show = get_post_meta( $post->ID, '_halloween_product_sku', true );</p><p>}elseif ( $show == 'price' ) {</p><p>$hs_show = $hween_options_arr['currency_sign']. get_post_meta( $post->ID, '_halloween_product_price', true );</p><p>}elseif ( $show == 'weight' ) {</p><p>$hs_show = get_post_meta( $post->ID, '_halloween_product_weight', true );</p><p>}elseif ( $show == 'color' ) {</p><p>$hs_show = get_post_meta( $post->ID, '_halloween_product_color', true );</p><p>}elseif ( $show == 'inventory' ) {</p><p>$hs_show = get_post_meta( $post->ID, '_halloween_product_inventory', true );</p><p>}</p><p>//return the shortcode value to display return $hs_show;</p><p>}</p><p>The fi rst thing you do is initialize the global variable $post. This will bring in the $post->ID value for the post in which you are using the shortcode. Next, you extract the shortcode attributes that you’ve defi ned, in this case show. Finally, you check what attribute value is being sent to the shortcode to determine what value to show. Using the shortcode like [hs show=price] would display the price of the product. If the price metadata is being displayed, you’ll need to retrieve the currency sign option value that was set by the user. Next up, you are going to create your products widget:</p><p>// Action hook to create plugin widget add_action( 'widgets_init', 'halloween_store_register_widgets' );</p><p>//register the widget</p><p> c08.indd 191 12/6/12 1:20 AM 192 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p> function halloween_store_register_widgets() {</p><p> register_widget( 'hs_widget' );</p><p>}</p><p>//hs_widget class class hs_widget extends WP_Widget {</p><p>First you have to register your widget as hs_widget using the register_widget() function. Next, you extend the Widget class as hs_widget. Now you need to create the four widget functions needed to build your widget: //process our new widget function hs_widget() {</p><p>$widget_ops = array( 'classname' => 'hs-widget-class', 'description' => __( 'Display Halloween Products', 'halloween-plugin' ) ); $this->WP_Widget( 'hs_widget', __( 'Products Widget', 'halloween-plugin'), $widget_ops );</p><p>}</p><p>The fi rst function you create is the hs_widget() function, also known as the constructor. Here, you set the widget title, description, and class name for your custom widget: //build our widget settings form function form( $instance ) {</p><p>$defaults = array( 'title' => __( 'Products', 'halloween-plugin' ), 'number_products' => '3' );</p><p>$instance = wp_parse_args( (array) $instance, $defaults ); $title = $instance['title']; $number_products = $instance['number_products']; ?> <p><?php _e('Title', 'halloween-plugin') ?>: <input class="widefat" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" /></p> <p><?php _e( 'Number of Products', 'halloween-plugin' ) ?>: <input name=" <?php echo $this->get_field_name( 'number_products' ); ?>" type="text" value="<?php echo esc_attr( $number_products ); ?>" size="2" maxlength="2" /> </p> <?php }</p><p>The second function you defi ne is the form() function. This builds the form for saving your widget settings. You are saving two settings in your widget: the widget title and the number of products to display. First, you defi ne the setting defaults if no settings have been saved. Next, you load in the </p><p> c08.indd 192 12/6/12 1:20 AM Creating a Plugin Example ❘ 193</p><p> saved values for your two settings. Finally, you display both setting form fi elds with the setting values if they exist. //save our widget settings function update( $new_instance, $old_instance ) {</p><p>$instance = $old_instance; $instance['title'] = sanitize_text_field( $new_instance['title'] ); $instance['number_products'] = absint( $new_instance['number_products'] );</p><p> return $instance;</p><p>}</p><p>The next function you create is the update() function. This function saves your widget settings. Notice how you utilize the sanitize_text_field()function to sanitize your widget title. You also use the PHP absint() function to verify that the number of products value is a non-negative integer. //display our widget function widget( $args, $instance ) { global $post;</p><p> extract( $args );</p><p> echo $before_widget; $title = apply_filters( 'widget_title', $instance['title'] ); $number_products = $instance['number_products'];</p><p> if ( ! empty( $title ) ) { echo $before_title . esc_html( $title ) . $after_title; };</p><p>//custom query to retrieve products $args = array( 'post_type' => 'halloween-products', 'posts_per_page' => absint( $number_products ) );</p><p>$dispProducts = new WP_Query(); $dispProducts->query( $args );</p><p> while ( $dispProducts->have_posts() ) : $dispProducts->the_post();</p><p>//load options array $hween_options_arr = get_option( 'halloween_options' );</p><p>//load custom meta values $hs_price = get_post_meta( $post->ID, '_halloween_product_price', true ); $hs_inventory = get_post_meta( $post->ID, '_halloween_product_inventory', true ); ?> <p> <a href="<?php the_permalink(); ?>" rel="bookmark" </p><p> c08.indd 193 12/6/12 1:20 AM 194 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p> title="<?php the_title_attribute(); ?> Product Information"> <?php the_title(); ?> </a> </p> <?php echo '<p>' .__( 'Price', 'halloween-plugin' ). ': ' .$hween_options_arr['currency_sign'] .$hs_price .'</p>';</p><p>//check if Show Inventory option is enabled if ( $hween_options_arr['show_inventory'] ) {</p><p>//display the inventory metadata for this product echo '<p>' .__( 'Stock', 'halloween-plugin' ). ': ' .$hs_inventory .'</p>';</p><p>} echo '<hr>';</p><p> endwhile;</p><p> wp_reset_postdata();</p><p> echo $after_widget;</p><p>} }</p><p>The fi nal function defi ned is the widget() function. This function displays your widget on the public side of your website. First you initialize the global $post variable and extract the $args for the widget. Then you display the $before_widget variable. This variable can be set by theme and plugin developers to display specifi ed content before and after the plugin. Next, you retrieve your two setting values. If the $title value is not empty, you use it, but if it is, you’ll use the default title you defi ned earlier.</p><p>To display the products in your widget, you are creating a custom Loop using WP_Query, as discussed in Chapter 5. Remember that because this is not your main Loop, you’ll want to use WP_Query to create your custom Loop instead of query_posts(). To defi ne your custom Loop, you pass in two parameters: one for the post type and one for number of products to display. The fi rst value (post_type=halloween-products) tells your custom Loop to only return Halloween product entries. The second value, posts_per_page, determines how many products to display. This number is pulled from the widget options value set by the user. Next, you load your option values and the custom metadata values you will be displaying in your widget. Finally, you display your product values in the widget. If the option Show Inventory is enabled, the inventory value will be displayed. After successfully creating the Products widget, it should look like Figure 8-8.</p><p>The fi nal step for your Halloween Store plugin is to create your unin- stall.php fi le: FIGURE 8-8: Products widget</p><p> c08.indd 194 12/6/12 1:20 AM Creating a Plugin Example ❘ 195</p><p><?php //if uninstall/delete not called from WordPress exit if( ! defined( 'ABSPATH' ) && ! defined( 'WP_UNINSTALL_PLUGIN' ) ) exit ();</p><p>// Delete options array from options table delete_option( 'halloween_options' ); ?></p><p>The fi rst thing you check is that ABSPATH and WP_UNINSTALL_PLUGIN constants exist. This means they were called from WordPress and add a layer of security on the uninstaller. After you have verifi ed that the request is valid, you delete your single option value from the database. You could also defi ne other uninstall functionality here, if needed, such as removing every product metadata value you saved in the database. That’s it! You just successfully built an entire plugin that includes many of the features covered in this chapter. This is a fairly basic plugin but should give you the examples and tools needed to expand upon. Listing 8-5 shows the plugin source code in its entirety. To access this code online, visit https://github.com/williamsba/HalloweenStore.</p><p>LISTING 8-5: Complete Plugin Source Code (halloween-store.zip)</p><p><?php /* Plugin Name: Halloween Store Plugin URI: http://webdevstudios.com/support/wordpress-plugins/ Description: Create a Halloween Store to display product information Version: 1.0 Author: Brad Williams Author URI: http://webdevstudios.com License: GPLv2 */</p><p>/* Copyright 2013 Brad Williams (email : brad@webdevstudios.com)</p><p>This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</p><p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</p><p>You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */</p><p>// Call function when plugin is activated continues</p><p> c08.indd 195 12/6/12 1:20 AM 196 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>LISTING 8-5 (continued)</p><p> register_activation_hook( __FILE__, 'halloween_store_install' );</p><p> function halloween_store_install() {</p><p>//setup default option values $hween_options_arr = array( 'currency_sign' => '$' );</p><p>//save our default option values update_option( 'halloween_options', $hween_options_arr );</p><p>}</p><p>// Action hook to initialize the plugin add_action( 'init', 'halloween_store_init' );</p><p>//Initialize the Halloween Store function halloween_store_init() {</p><p>//register the products custom post type $labels = array( 'name' => __( 'Products', 'halloween-plugin' ), 'singular_name' => __( 'Product', 'halloween-plugin' ), 'add_new' => __( 'Add New', 'halloween-plugin' ), 'add_new_item' => __( 'Add New Product', 'halloween-plugin' ), 'edit_item' => __( 'Edit Product', 'halloween-plugin' ), 'new_item' => __( 'New Product', 'halloween-plugin' ), 'all_items' => __( 'All Products', 'halloween-plugin' ), 'view_item' => __( 'View Product', 'halloween-plugin' ), 'search_items' => __( 'Search Products', 'halloween-plugin' ), 'not_found' => __( 'No products found', 'halloween-plugin' ), 'not_found_in_trash' => __( 'No products found in Trash', 'halloween- plugin' ), 'menu_name' => __( 'Products', 'halloween-plugin' ) );</p><p>$args = array( 'labels' => $labels, 'public' => true, 'publicly_queryable' => true, 'show_ui' => true, 'show_in_menu' => true, 'query_var' => true, 'rewrite' => true, 'capability_type' => 'post', 'has_archive' => true, 'hierarchical' => false, 'menu_position' => null, 'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' )</p><p> c08.indd 196 12/6/12 1:20 AM Creating a Plugin Example ❘ 197</p><p>); </p><p> register_post_type( 'halloween-products', $args );</p><p>}</p><p>// Action hook to add the post products menu item add_action( 'admin_menu', 'halloween_store_menu' );</p><p>//create the Halloween Masks sub-menu function halloween_store_menu() {</p><p> add_options_page( __( 'Halloween Store Settings Page', 'halloween-plugin' ), __( 'Halloween Store Settings', 'halloween-plugin' ), 'manage_options', 'halloween-store-settings', 'halloween_store_settings_page' );</p><p>}</p><p>//build the plugin settings page function halloween_store_settings_page() {</p><p>//load the plugin options array $hween_options_arr = get_option( 'halloween_options' );</p><p>//set the option array values to variables $hs_inventory = ( ! empty( $hween_options_arr['show_inventory'] ) ) ? $hween_options_arr['show_inventory'] : ''; $hs_currency_sign = $hween_options_arr['currency_sign']; ?> <div class="wrap"> <h2><?php _e( 'Halloween Store Options', 'halloween-plugin' ) ?></h2></p><p><form method="post" action="options.php"> <?php settings_fields( 'halloween-settings-group' ); ?> <table class="form-table"> <tr valign="top"> <th scope="row"><?php _e( 'Show Product Inventory', 'halloween-plugin' ) ?></th> <td><input type="checkbox" name="halloween_options[show_inventory]" <?php echo checked( $hs_inventory, 'on' ); ?> /></td> </tr></p><p><tr valign="top"> <th scope="row"><?php _e( 'Currency Sign', 'halloween-plugin' ) ?></th> <td><input type="text" name="halloween_options[currency_sign]" value="<?php echo esc_attr( $hs_currency_sign ); ?>" size="1" maxlength="1" /></td> </tr> </table></p><p><p class="submit"> <input type="submit" class="button-primary" value="<?php _e( 'Save Changes', 'halloween-plugin' ); ?>" /> continues</p><p> c08.indd 197 12/6/12 1:20 AM 198 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>LISTING 8-5 (continued)</p></p><p></form> </div> <?php }</p><p>// Action hook to register the plugin option settings add_action( 'admin_init', 'halloween_store_register_settings' );</p><p> function halloween_store_register_settings() {</p><p>//register the array of settings register_setting( 'halloween-settings-group', 'halloween_options', 'halloween_sanitize_options' );</p><p>}</p><p> function halloween_sanitize_options( $options ) {</p><p>$options['show_inventory'] = ( ! empty( $options['show_inventory'] ) ) ? sanitize_text_field( $options['show_inventory'] ) : ''; $options['currency_sign'] = ( ! empty( $options['currency_sign'] ) ) ? sanitize_text_field( $options['currency_sign'] ) : '';</p><p> return $options;</p><p>}</p><p>//Action hook to register the Products meta box add_action( 'add_meta_boxes', 'halloween_store_register_meta_box' );</p><p> function halloween_store_register_meta_box() {</p><p>// create our custom meta box add_meta_box( 'halloween-product-meta', __( 'Product Information','halloween-plugin' ), 'halloween_meta_box', 'halloween-products', 'side', 'default' );</p><p>}</p><p>//build product meta box function halloween_meta_box( $post ) {</p><p>// retrieve our custom meta box values $hween_sku = get_post_meta( $post->ID, '_halloween_product_sku', true ); $hween_price = get_post_meta( $post->ID, '_halloween_product_price', true ); $hween_weight = get_post_meta( $post->ID, '_halloween_product_weight', true ); $hween_color = get_post_meta( $post->ID, '_halloween_product_color', true ); $hween_inventory = get_post_meta( $post->ID,</p><p> c08.indd 198 12/6/12 1:20 AM Creating a Plugin Example ❘ 199</p><p>'_halloween_product_inventory', true );</p><p>//nonce field for security wp_nonce_field( 'meta-box-save', 'halloween-plugin' );</p><p>// display meta box form echo '<table>'; echo '<tr>'; echo '<td>' .__('Sku', 'halloween-plugin').':</td><td> <input type="text" name="halloween_product_sku" value="'.esc_attr( $hween_sku ).'" size="10"></td>'; echo '</tr><tr>'; echo '<td>' .__('Price', 'halloween-plugin').':</td><td> <input type="text" name="halloween_product_price" value="'.esc_attr( $hween_price ).'" size="5"></td>'; echo '</tr><tr>'; echo '<td>' .__('Weight', 'halloween-plugin').':</td><td> <input type="text" name="halloween_product_weight" value="'.esc_attr( $hween_weight ).'" size="5"></td>'; echo '</tr><tr>'; echo '<td>' .__('Color', 'halloween-plugin').':</td><td> <input type="text" name="halloween_product_color" value="'.esc_attr( $hween_color ).'" size="5"></td>'; echo '</tr><tr>'; echo '<td>Inventory:</td><td> <select name="halloween_product_inventory" id="halloween_product_inventory"> <option value="In Stock"' .selected( $hween_inventory, 'In Stock', false ). '>' .__( 'In Stock', 'halloween-plugin' ) . '</option> <option value="Backordered"' .selected( $hween_inventory, 'Backordered', false ). '>' .__( 'Backordered', 'halloween-plugin' ) . '</option> <option value="Out of Stock"' .selected( $hween_inventory, 'Out of Stock', false ) . '>' .__( 'Out of Stock', 'halloween-plugin' ). '</option> <option value="Discontinued"' .selected( $hween_inventory, 'Discontinued', false ) . '>' .__( 'Discontinued', 'halloween-plugin' ). '</option> </select></td>'; echo '</tr>';</p><p>//display the meta box shortcode legend section echo '<tr><td colspan="2"><hr></td></tr>'; echo '<tr><td colspan="2"><strong>' .__( 'Shortcode Legend', 'halloween-plugin' ).'</strong></td></tr>'; echo '<tr><td>' .__( 'Sku', 'halloween-plugin' ) .':</td><td> [hs show=sku]</td></tr>'; echo '<tr><td>' .__( 'Price', 'halloween-plugin' ).':</td><td> [hs show=price]</td></tr>'; echo '<tr><td>' .__( 'Weight', 'halloween-plugin' ).':</td><td> [hs show=weight]</td></tr>'; echo '<tr><td>' .__( 'Color', 'halloween-plugin' ).':</td><td> continues</p><p> c08.indd 199 12/6/12 1:20 AM 200 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>LISTING 8-5 (continued)</p><p>[hs show=color]</td></tr>'; echo '<tr><td>' .__( 'Inventory', 'halloween-plugin' ).':</td><td> [hs show=inventory]</td></tr>'; echo '</table>'; }</p><p>// Action hook to save the meta box data when the post is saved add_action( 'save_post','halloween_store_save_meta_box' );</p><p>//save meta box data function halloween_store_save_meta_box( $post_id ) {</p><p>//verify the post type is for Halloween Products and metadata has been posted if ( get_post_type( $post_id ) == 'halloween-products' && isset( $_POST['halloween_product_sku'] ) ) {</p><p>//if autosave skip saving data if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;</p><p>//check nonce for security check_admin_referer( 'meta-box-save', 'halloween-plugin' );</p><p>// save the meta box data as post metadata update_post_meta( $post_id, '_halloween_product_sku', sanitize_text_field( $_POST['halloween_product_sku'] ) ); update_post_meta( $post_id, '_halloween_product_price', sanitize_text_field( $_POST['halloween_product_price'] ) ); update_post_meta( $post_id, '_halloween_product_weight', sanitize_text_field( $_POST['halloween_product_weight'] ) ); update_post_meta( $post_id, '_halloween_product_color', sanitize_text_field( $_POST['halloween_product_color'] ) ); update_post_meta( $post_id, '_halloween_product_inventory', sanitize_text_field( $_POST['halloween_product_inventory'] ) );</p><p>}</p><p>}</p><p>// Action hook to create the products shortcode add_shortcode( 'hs', 'halloween_store_shortcode' );</p><p>//create shortcode function halloween_store_shortcode( $atts, $content = null ) { global $post;</p><p> extract( shortcode_atts( array( "show" => '' ), $atts ) );</p><p>//load options array</p><p> c08.indd 200 12/6/12 1:20 AM Creating a Plugin Example ❘ 201</p><p>$hween_options_arr = get_option( 'halloween_options' );</p><p> if ( $show == 'sku') {</p><p>$hs_show = get_post_meta( $post->ID, '_halloween_product_sku', true );</p><p>}elseif ( $show == 'price' ) {</p><p>$hs_show = $hween_options_arr['currency_sign']. get_post_meta( $post->ID, '_halloween_product_price', true );</p><p>}elseif ( $show == 'weight' ) {</p><p>$hs_show = get_post_meta( $post->ID, '_halloween_product_weight', true );</p><p>}elseif ( $show == 'color' ) {</p><p>$hs_show = get_post_meta( $post->ID, '_halloween_product_color', true );</p><p>}elseif ( $show == 'inventory' ) {</p><p>$hs_show = get_post_meta( $post->ID, '_halloween_product_inventory', true );</p><p>}</p><p>//return the shortcode value to display return $hs_show; }</p><p>// Action hook to create plugin widget add_action( 'widgets_init', 'halloween_store_register_widgets' );</p><p>//register the widget function halloween_store_register_widgets() {</p><p> register_widget( 'hs_widget' );</p><p>}</p><p>//hs_widget class class hs_widget extends WP_Widget {</p><p>//process our new widget function hs_widget() {</p><p>$widget_ops = array( 'classname' => 'hs-widget-class', 'description' => __( 'Display Halloween Products','halloween- plugin' ) ); $this->WP_Widget( 'hs_widget', __( 'Products Widget','halloween-plugin'), $widget_ops );</p><p>}</p><p>//build our widget settings form continues</p><p> c08.indd 201 12/6/12 1:20 AM 202 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>LISTING 8-5 (continued)</p><p> function form( $instance ) {</p><p>$defaults = array( 'title' => __( 'Products', 'halloween-plugin' ), 'number_products' => '3' );</p><p>$instance = wp_parse_args( (array) $instance, $defaults ); $title = $instance['title']; $number_products = $instance['number_products']; ?> <p><?php _e('Title', 'halloween-plugin') ?>: <input class="widefat" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" /></p> <p><?php _e( 'Number of Products', 'halloween-plugin' ) ?>: <input name="<?php echo $this->get_field_name( 'number_products' ); ?>" type="text" value="<?php echo esc_attr( $number_products ); ?>" size="2" maxlength="2" /> </p> <?php }</p><p>//save our widget settings function update( $new_instance, $old_instance ) {</p><p>$instance = $old_instance; $instance['title'] = sanitize_text_field( $new_instance['title'] ); $instance['number_products'] = absint( $new_instance['number_products'] );</p><p> return $instance;</p><p>}</p><p>//display our widget function widget( $args, $instance ) { global $post;</p><p> extract( $args );</p><p> echo $before_widget; $title = apply_filters( 'widget_title', $instance['title'] ); $number_products = $instance['number_products'];</p><p> if ( ! empty( $title ) ) { echo $before_title . esc_html( $title ) . $after_title; };</p><p>//custom query to retrieve products $args = array( 'post_type' => 'halloween-products',</p><p> c08.indd 202 12/6/12 1:20 AM Publishing to the Plugin Directory ❘ 203</p><p>'posts_per_page' => absint( $number_products ) );</p><p>$dispProducts = new WP_Query(); $dispProducts->query( $args );</p><p> while ( $dispProducts->have_posts() ) : $dispProducts->the_post();</p><p>//load options array $hween_options_arr = get_option( 'halloween_options' );</p><p>//load custom meta values $hs_price = get_post_meta( $post->ID, '_halloween_product_price', true ); $hs_inventory = get_post_meta( $post->ID, '_halloween_product_inventory', true ); ?> <p> <a href="<?php the_permalink(); ?>" rel="bookmark" title="<?php the_title_attribute(); ?> Product Information"> <?php the_title(); ?> </a> </p> <?php echo '<p>' .__( 'Price', 'halloween-plugin' ). ': ' .$hween_options_arr['currency_sign'] .$hs_price .'</p>';</p><p>//check if Show Inventory option is enabled if ( $hween_options_arr['show_inventory'] ) {</p><p>//display the inventory metadata for this product echo '<p>' .__( 'Stock', 'halloween-plugin' ) . ': ' .$hs_inventory .'</p>';</p><p>} echo '<hr>';</p><p> endwhile;</p><p> wp_reset_postdata();</p><p> echo $after_widget;</p><p>}</p><p>}</p><p>PUBLISHING TO THE PLUGIN DIRECTORY</p><p>Now it’s time to release your plugin to the world! Releasing your plugin on WordPress.org is not a requirement, but it is the best way to get your plugin publicized and have other WordPress users download and install it. Remember that the Plugin Directory on WordPress.org is directly hooked to every installation of WordPress, so if your plugin exists in the directory then anyone running WordPress can easily download and install it.</p><p> c08.indd 203 12/6/12 1:20 AM 204 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>Restrictions A few restrictions exist to submitting your plugin to the Plugin Directory: ➤ Plugin must be compatible with GPLv2 or any later version. ➤ Plugin must not do anything illegal or morally offensive. ➤ Must use the Subversion (SVN) repository to host your plugin. ➤ Plugin must not embed external links on the user’s site (such as a “powered by” link) with- out asking the plugin user’s permission. Make sure to follow these guidelines or your plugin will be removed from the Plugin Directory.</p><p>Submitting Your Plugin The fi rst step is to create an account on WordPress.org if you don’t already have one. To register a new account, visit the registration page at http://wordpress.org/support/register.php. This WordPress.org account is used in the Plugin Directory as well as the support forums. After you have registered your account and signed in, it’s time to submit your plugin for inclusion in the Plugin Directory on WordPress.org. To submit your plugin, visit the Add Your Plugin page at http://wordpress.org/extend/plugins/add/. The fi rst required fi eld is the Plugin Name. The plugin name should be the exact name you want to use for your plugin. Keep in mind that the plugin name will be used as the URL in the directory. For example, if you submit a plugin named WP Brad, the URL to your plugin in the Plugin Directory will be http://wordpress.org/extend/plugins/wp-brad/. As you can see, the name you insert here is very important and cannot be changed. The second required fi eld is the Plugin Description. This fi eld should contain a detailed description about your plugin. Remember that the description is really the only information used to decide whether or not to allow your plugin in the directory. Clearly state the plugin functionality, the purpose of the plugin, and installation instructions for the plugin. The fi nal fi eld is the Plugin URL. This is not a required fi eld, but it’s highly recommended that you include a download link to your plugin. This enables the reviewer of your plugin to download and look at your plugin if needed. Again this is not a required fi eld but you are strongly encouraged to fi ll it in. After you have fi lled out all of the information, click the Send Post button to submit your plugin request. The Plugin Directory states, “Within some vaguely defi ned amount of time, someone will approve your request.” This doesn’t really tell you much, but most plugins are approved within a day or so. The fact that your plugin has been approved does not mean you are done. The next step is to upload your plugin to the Subversion Repository that has been created for it.</p><p>Creating a readme.txt File One fi le that is required to submit your plugin to the Plugin Directory is readme.txt. This fi le is used to fi ll in all of the plugin information on the Plugin detail page in the Directory. WordPress </p><p> c08.indd 204 12/6/12 1:20 AM Publishing to the Plugin Directory ❘ 205</p><p> has developed the readme fi le standard, which details exactly how your readme.txt fi le should be defi ned. Here’s an example readme.txt fi le: === Plugin Name === Contributors: williamsba1, messenlehner, ericlewis, jtsternberg Donate link: http://example.com/donate Tags: admin, post, images, page, widget Requires at least: 3.0 Tested up to: 3.5 Stable tag: 1.1.0.0 License: GPLv2</p><p>Short description of the plugin with 150 chars max. No markup here.</p><p>== Description ==</p><p>This is the long description. No limit, and you can use Markdown</p><p>Additional plugin features</p><p>* Feature 1 * Feature 2 * Feature 3</p><p>For support visit the [Support Forum](http://example.com/forum/ " Support Forum")</p><p>== Installation ==</p><p>1. Upload 'plugin-directory' to the '/wp-content/plugins/' directory 2. Activate the plugin through the 'Plugins' SubPanel in WordPress 3. Place '<?php gmp_custom_function(); ?>' in your theme templates</p><p>== Frequently Asked Questions ==</p><p>= A question that someone might have =</p><p>An answer to that question.</p><p>= Does this plugin work with WordPress Multisite? =</p><p>Absolutely! This plugin has been tested and verified to work on the most current version of WordPress Multisite</p><p>== Screenshots ==</p><p>1. Screenshot of plugin settings page 2. Screenshot of plugin in action</p><p>== Changelog ==</p><p>= 1.1 = * New feature details * Bug fix details</p><p>= 1.0 =</p><p> c08.indd 205 12/6/12 1:20 AM 206 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>* First official release</p><p>== Upgrade Notice ==</p><p>= 1.1 = * Security bug fixed</p><p>For an online readme.txt example, visit the Readme Standard at http://wordpress.org/extend/ plugins/about/readme.txt.</p><p>WordPress.org also features a readme.txt validator so you can verify you have a properly formatted readme.txt fi le before submitting to the Subversion directory. You can access the validator at http://wordpress.org/extend/plugins/about/validator/. Let’s break down the individual readme.txt sections: === Plugin Name === Contributors: williamsba1, messenlehner, ericlewis, jtsternberg Donate link: http://example.com/donate Tags: admin, post, images, page, widget Requires at least: 3.0 Tested up to: 3.5 Stable tag: 1.1.0.0 License: GPLv2</p><p>Short description of the plugin with 150 chars max. No markup here.</p><p>The Plugin Name section is one of the most important parts of your readme.txt fi le. The fi rst line lists the contributors to the plugin. This is a comma-separated list of WordPress.org usernames that helped contribute to the plugin. The donate link should be a URL to either a donate link or a web page that explains how users can donate to the plugin author. This is a great place for a PayPal donation link. Tags are a comma-separated list of tags describing your plugin. The “Requires at least” fi eld is the minimal version of WordPress required to run the plugin. If your plugin won’t run on anything prior to 3.0, then 3.0 would be the “Requires at least” value. Likewise, “Tested up to” is the latest version the plugin has been tested on. This will typically be the latest stable version of WordPress. The Stable tag is also a very important fi eld and should be the current version of the plugin. This value should always match the version number listed in the plugin header. Last is a short description of the plugin, which should be no more than 150 characters and cannot contain any markup. == Description ==</p><p>This is the long description. No limit, and you can use Markdown</p><p>Additional plugin features</p><p>* Feature 1 * Feature 2 * Feature 3</p><p>For support visit the [Support Forum](http://example.com/forum/ " Support Forum")</p><p> c08.indd 206 12/6/12 1:20 AM Publishing to the Plugin Directory ❘ 207</p><p>The Description section features a detailed description of your plugin. This is the default information displayed on the plugin detail page in the Plugin Directory. There is no limit to the length of the description. You can also use unordered lists, shown in the preceding example, and ordered lists in your description. Links can also be inserted. == Installation ==</p><p>1. Upload 'plugin-directory' to the '/wp-content/plugins/' directory 2. Activate the plugin through the 'Plugins' SubPanel in WordPress 3. Place '<?php prowp_custom_function(); ?>' in your theme templates The Installation section details the steps involved to install a plugin. If your plugin has very specifi c installation requirements, make sure they are listed here in detail. It’s also a good idea to list the function name and shortcode that can be used with the plugin. == Frequently Asked Questions ==</p><p>= A question that someone might have =</p><p>An answer to that question.</p><p>= Does this plugin work with WordPress Multisite? =</p><p>Absolutely! This plugin has been tested and verified to work on the most current version of WordPress Multisite The FAQ section is the perfect place to list frequently asked questions, of course! This helps answer commonly asked questions and can eliminate many support requests. You can list multiple questions with answers, as this example shows: == Screenshots ==</p><p>1. Screenshot of plugin settings page 2. Screenshot of plugin in action The Screenshots section is used to add individual screenshots of your plugin to the plugin detail page. This is actually a two-step process. The fi rst step is to list out each screenshot description in an ordered list. The next step is to place image fi les in your trunk directory (which is discussed in more detail next). These images fi le names must match the listing number. For instance, the screen- shot of your settings page should be named screenshot-1.png. The screenshot of your plugin in action should be named screenshot-2.png. The fi le types accepted are png, jpg, jpeg, and gif. == Changelog ==</p><p>= 1.1 = * New feature details * Bug fix details</p><p>= 1.0 = * First official release The next section is the Changelog. This section is important for listing out what each plugin version release has added or fi xed. This is a very helpful section for anyone looking to upgrade to the latest version. It’s always nice to know exactly what is being added and fi xed to determine how critical the </p><p> c08.indd 207 12/6/12 1:20 AM 208 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p> plugin update is. A new item should be added for each version you release to the Plugin Directory, regardless of how minor that update may be. == Upgrade Notice ==</p><p>= 1.1 = * Security bug fixed The fi nal section is the Upgrade Notice section. This section allows you to send specifi c upgrade notice messages to the WordPress user. These messages are shown on the Dashboard ➪ Updates Subpanel when a new version of your plugin is released.</p><p>The readme.txt fi le can also accept arbitrary sections in the same format as the rest. This is useful for more complicated plugins that need to provide additional information. Arbitrary sections will be displayed below the built-in sections described previously.</p><p>Setting Up SVN The Plugin Directory uses Subversion (SVN) for handling plugins. To publish your plugin to the directory, you’ll need to set up and confi gure an SVN client. In this example, you are going to use TortoiseSVN for Windows. TortoiseSVN is a free GUI client interface for SVN. For a list of additional SVN clients for different platforms visit http://subversion.apache.org/.</p><p>First you’ll need to download the appropriate installer at http://tortoisesvn.net/downloads .html. After installing TortoiseSVN, you’ll need to reboot your computer. The next step is to create a new directory on your computer to store your plugin fi les. It is recommended that you make a folder to store all of your plugins in, such as c:\projects\wordpress-plugins. This makes it much easier going forward if you create and release multiple plugins to WordPress.org.</p><p>Next, navigate to your new wordpress-plugins directory and create a new directory for your plugin. Right-click this new folder to pull up a context menu. You’ll notice the new TortoiseSVN options listed: SVN Checkout and ToirtoiseSVN. Select SVN Checkout and a dialog box appears, as shown in Figure 8-9. The URL of the repository was provided to you in the e-mail you received when your plugin was approved. This URL should be the same as the plugin URL so in this example the URL would be http://plugins.svn.wordpress.org /wp-brad. The Checkout directory is the local folder in which to store your plugin. In this FIGURE 8-9: SVN Checkout dialog box case, you will use the new folder you created at c:\projects\wordpress-plugins\wp-brad. Make sure Checkout Depth is set to Fully Recursive. Also verify that the Revision is set to HEAD Revision. Finally, click OK. TortoiseSVN will connect to the SVN Repository for your plugin and, if all goes well, will create three new directories in your folder called branches, tags, and trunk. These three folders each serve a specifi c purpose for SVN:</p><p> c08.indd 208 12/6/12 1:20 AM Publishing to the Plugin Directory ❘ 209</p><p>1. Branches — Every time a new major version is released, it gets a branch. This allows for bug fi xes without releasing new functionality from trunk. 2. Tags — Every time a new version is released, you’ll make a new tag for it. 3. Trunk — Main development area. The next major release of code lives here. Now that you’ve connected to your plugin’s SVN Repository, you need to move your plugin fi les to the trunk directory. Remember to also place your readme.txt fi le and any screenshots, includes, and so on in the trunk directory for your plugin. Remember that you’re just staging the plugin fi les to publish to the plugin directory. Publishing the fi les to WordPress.org is covered in the next section.</p><p>Once you’ve verifi ed all of the plugin fi les are in trunk, you are ready to publish your plugin to the Plugin Directory!</p><p>Publishing to the Plugin Directory Publishing your plugin to the Plugin Directory is a two-step process. First you need to SVN Commit the trunk folder to your SVN Repository. Second, you need to tag your plugin release. Once both steps have been completed, your new plugin will appear in the Plugin Directory within about 15 minutes.</p><p>To commit your plugin trunk, simply right-click the trunk folder and select SVN Commit. You’ll be presented with a dialog box to enter a log message and to select which fi les to commit to the trunk. Fill in a brief log message, such as “Adding WP-Brad 1.1,” and select all of the fi les you want to commit. TortoiseSVN will automatically select all fi les that have changed so you probably won’t need to change this. Next, click OK and you will be prompted to enter a username and password. This is the username and password you created on WordPress.org. Next, you need to tag your plugin version. To tag your plugin version, simply right-click the trunk directory and select TortoiseSVN ➪ Branch/tag from the context menu. In the dialog box that appears, fi ll in the path to your tag directory. Using this example, the URL would be http:// plugins.svn.wordpress.org/wp-brad/tags/1.1.0.0/. This tag version should match the stable tag in your plugin’s readme.txt fi le — in your case, version 1.1.0.0. Also type in a log message, such as “tagging version 1.1.0.0” and verify that “HEAD revision in the repository” is selected for the Create Copy option. Click OK and your plugin will create a new directory in your tags folder for version 1.1.0.0 with the appropriate plugin fi les. That’s it! If everything worked successfully, your plugin should appear in the Plugin Directory within about 15 minutes. Once your plugin is successfully published you’ll want to verify that all of the information is correct. One way to verify that your plugin was published successfully is to visit the Subversion URL, which for this example would be http://plugins.svn.wordpress.org/wp- brad/. Here you can ensure the trunk and tag directories were uploaded successfully. After 15 minutes, you can also verify your plugin by visiting the offi cial Plugin Directory page at http://www.wordpress.org/extend/plugins/wp-brad. If you need to make any changes to your readme.txt fi le, simply edit it locally in your trunk folder, right-click the fi le, and click SVN Commit.</p><p> c08.indd 209 12/6/12 1:20 AM 210 ❘ CHAPTER 8 PLUGIN DEVELOPMENT</p><p>Releasing a New Version A great feature of WordPress plugins is that you can easily release updates for your plugins in the Plugin Directory. When a new plugin version is released, a notice is displayed on any WordPress site that currently has that plugin uploaded to its server, whether or not it is activated. The user can use the automatic upgrade process to easily upgrade the plugin to the latest version. This is especially important if there are security patches in your plugin to help keep WordPress secure.</p><p>To release a new plugin version, make sure you copy the updated plugin fi les to the /trunk directory you set up earlier. This folder should contain all fi les for the updated plugin version. Once you have verifi ed that all of the updates plugin fi les exist, simply right-click the trunk directory and select SVN Commit. Remember to type in a brief message such as “Committing version 1.2.” TortoiseSVN should have already selected all of the fi les that have changed, but if not, select all of the fi les you want to publish and click OK.</p><p>The fi nal step is to tag your new version. To tag your new release, right-click the trunk directory and select TortoiseSVN ➪ Branch/tag. For this example, the URL would be http://plugins.svn. wordpress.org/wp-brad/tags/1.2.0.0/. Remember to write a brief log entry such as “Tagging version 1.2” and click OK. That’s it! Your new plugin version will be published in the Plugin Directory within 15 minutes. After the new version has been released, your plugin will appear at the top of the Recently Updated Plugins list on WordPress.org. The WordPress Plugin Directory is a great source for inspiration and reference when building cus- tom plugins. Don’t be scared to look at another plugin source code for reference. Find a plugin that functions similarly to what you want and see how the plugin author structured the code or used hooks to interpose his or her plugin ideas in the WordPress core processing.</p><p>SUMMARY</p><p>In this chapter you learned WordPress plugin packaging including the required plugin header, including a WordPress compatible software license with your plugin, and activating and deactivating functions. You also covered very important data validation and sanitization for plugin security. The chapter also covered powerful hooks, plugin setting options, and multiple ways to integrate your plugins into WordPress. Plugins are only half of the WordPress extensibility story, giving you the power to add custom functions and event-driving processing to your site. If you want to change the look and feel of your site, change the way in which WordPress displays posts, or provide slots for those widgets you created, you’ll want to extend WordPress through theme development which is the focus of chapter 9.</p><p> c08.indd 210 12/6/12 1:20 AM 9 Theme Development</p><p>WHAT’S IN THIS CHAPTER?</p><p>➤ Understanding the various fi les and templates that constitute a theme ➤ Modifying an existing theme to meet your own needs ➤ Identify the diff erent reasons to use a project theme or a child theme ➤ Creating a new theme based on the Twenty Eleven theme </p><p>Content is king, right? That is certainly true. Nothing is going to drive visitors to your site, and keep them coming back, except for your content. Even if you have the best content on the Internet for your topic, you have to present it to the reader, the browser, and to the search engines so that your content can be consumed. That’s where themes come in. Themes control the presentation layer of your site, including both the user experience and how it is offered to the consumer. It also controls the logic that determines which type of page and, therefore, which type of loop is to be used. This chapter reviews how to install a theme on your website and then takes you through the various aspects of a theme and how they apply to the presentation of your content. You will also review different strategies for creating your theme, whether specifi cally for a project, or as an adaptation of a theme framework. By the end of this chapter, you will have an understanding of theme functionality and a solid foundation on which to build your own custom project or child themes from scratch for use in your own projects.</p><p>WHY USE A THEME?</p><p>Your website theme is essentially the face of your website. It’s what makes the fi rst impression on your visitor. Even though surely none of us is shallow enough to judge a book simply by its cover, if your website has valuable content but your theme makes the content hard to read, </p><p> c09.indd 211 12/6/12 1:20 AM 212 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p> hard to fi nd, or generally inaccessible in any way, or your site is slow to load, not to mention downright ugly, you have probably lost that visitor. You may never have had that visitor to lose. The theme accomplishes many things for your website. Generally, people think of the theme as the appearance of your site. It is the look and feel that gives your website that certain style or fl air. It is the x-factor that gives your site a personality and makes your site stand out from the crowd. Your theme is all that; a picture really is worth a thousand words. But this is simply the graphical aspect of your site; your theme is so much more than the cohesive marketing and branding facade. Your theme encompasses the entire user experience and more. It controls what content gets rendered, including any error conditions. Your theme converts your content and look and feel into the raw HTML that is delivered to the browser through its various templates. In general, that’s what this chapter is about: using your theme to structure and control the content delivery and the overall personality of your website. Your theme also has other functions, including user experience and search engine optimization, which are addressed in later chapters.</p><p>INSTALLING A THEME</p><p>Starting back in 2010, WordPress began shipping a new default theme each year. The Twenty Ten theme, released in — you guessed it, 2010 — was the fi rst to replace the venerable Kubrick theme that had been around since 2005. Last year, Automattic released the Twenty Eleven theme as the new default, and the new Twenty Twelve theme is expected to release with WordPress 3.5. These default themes are all pretty solid themes to use for your site, but they are the defaults. That means you see them on many sites across the web. Being unique, or custom, may or may not be important to you or your goals, but for the sake of this chapter, pretend it is and you don’t want to use a stock, out-of-the box theme. How do you make WordPress use a new theme? First, you either have to fi nd one you like or make one. Countless WordPress theme resources are available, and they all vary in quality. It is best to try some out and see how they work with your content and if they match the personality and branding you want your site to convey. You have two simple ways to activate a new theme on your website. There is the traditional FTP installation, and as of WordPress 2.8 there is a new integrated theme browser and installer. The Theme Browser is limited in that it allows you to install themes only from the sanctioned WordPress Theme Directory on WordPress.org. This is not inherently bad because plenty of solid, good-looking themes are in the Theme Directory and they are all GPL licensed and free (two of the requirements for being listed in the Directory). However, the Directory is a limited market; heaps of other sites offer valuable WordPress themes, still of varying quality, both for free and for premiums. In order to install these non-Directory themes, you will have to use the FTP method.</p><p>FTP Installation File Transfer Protocol, or FTP, is the old standby for transferring fi les from your local workstation to the server. If your host supports it, you should use a secure form of transfer, such as SFTP or SCP, to move the fi les, but the concepts here are similar.</p><p> c09.indd 212 12/6/12 1:21 AM What Is a Theme? ❘ 213</p><p>Download the theme package that you would like to try to your local computer and unzip it to a local directory. If you have shell access on your server, you can unzip on the server to save in transfer time. Using your FTP client, connect to your web server. FTP or copy the fi les into your themes directory of your site. Your themes folder is located in /example.com/wp-content/themes/. Once your theme fi les are on the server, log in to your site’s WordPress Control Panel. Select Appearance, select your theme to preview it, and then activate it. Your website is now using the new theme.</p><p>Theme Installer WordPress 2.8 introduced a new Theme Installer. Currently this is offered as new tab on your Themes Control Panel. The Manage Themes tab reveals which themes are currently available to your WordPress installation. The second tab, Install Themes, allows the site administrator (or anyone else with proper permissions) to search and fi lter the online WordPress Theme Directory for existing published themes. All themes in the Directory are subject to certain conditions in order to be listed; most notably, they must be licensed to be GPL-compatible. This new Theme Installer is pretty slick. You simply work the fi lters and search terms to browse the Directory. Browse the thumbnail screenshots until you fi nd one you like. Then you can either click Install Now to make it available to your current WordPress installation. Or you can preview the theme in a larger format with a variety of HTML elements displayed for you to examine the CSS styling. Finally, you can read the theme details and user star ratings by clicking the Details link. On a development system running Microsoft Windows 7 and WAMP, this just works, which is a little disconcerting. It is a permissions issue in your Webroot. Although this raises some concerns about what else could so easily be installed on the site, in this case, it is just a development machine, and the convenience of being able to test drive new themes outweighs the concerns. Trying the Theme Installer on a production server for a WordPress site running on Ubuntu Linux may yield different results. After selecting an appropriate obnoxious theme to try out, the Theme Installer asks for FTP credentials to put the fi les on the server, in this case because the fi le security permissions on the production server are properly set for production and to not allow this sort of thing. Again, there is some concern about the actual security implications of giving out FTP credentials that are required to proceed. This is similar to how the WordPress core updates and plugin updates work. See Chapter 13 on securing your WordPress installation for information about directory permissions. In short, the Theme Installer is really slick and convenient for development to test out new themes, but because of possible security implications, carefully consider its use in a production environment. The balance of convenience and security is often a diffi cult choice.</p><p>WHAT IS A THEME?</p><p>What actually makes up a theme? You have an idea of what themes do, but how do they do it and what’s really involved? As previously mentioned, a theme does several things, including structuring your content and providing the personality of your website. This is done through a combination of </p><p> c09.indd 213 12/6/12 1:21 AM 214 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p> fi les and fi le types. You will notice a mix of PHP fi les, CSS fi les, and JavaScript files in the theme. A good WordPress theme keeps the style, which is CSS, separate from the structure and logic, which make up the PHP fi les. Although there are always reasons for breaking the rules, striving to keep these separate will improve the maintainability and effi ciency of your theme. Each theme has variations on these fi les and each theme’s fi les are different.</p><p>Template Files Template fi les are the meat of your theme. Template fi les are PHP code fi les that control what content is shown to your visitor. These fi les also generate the HTML for the browser to control how your content is shown. WordPress actually decides which template fi le to use based on the content requested. Certain template fi les are used for different tasks. At first glance, the quantity of template fi les in a theme can be daunting. Although each theme is different, some have only a couple of fi les, while others can be very complex. After you learn the different fi les involved in a theme, you will review the template hierarchy, which is the mechanism WordPress uses to determine which templates to use when. This template hierarchy, covered later in this chapter and the numerous types of template fi les available can be overwhelming when you are starting out on theme design, but you will develop an appreciation for the power of this setup. This fl exibility allows for a huge amount of control over your site and what is delivered to the browser, which is the beauty of WordPress and defi nitely one of its strongest traits.</p><p>CSS WordPress themes truly strive to separate content from style. A theme developer can ignore these guidelines and create a poorly divided theme, but a good theme developer does this well. A theme must have at least one cascading style sheet. The primary style sheet for the theme must be named style.css. In addition, the fi rst few lines of this style sheet fi le must adhere to certain guidelines. These specifi c requirements are covered later in this chapter in the “Style.css” section. WordPress uses this information to determine which themes are available to the WordPress site and to make them show up in the Appearance Control Panel. If the style sheet header is not coded to the standard expectation of WordPress, it will not show up in the Control Panel. The style sheet is just what it sounds like. It is where you put all your CSS styles. How you structure it, or what you do with it, is entirely up to the theme developer. CSS development is both an art and a science and a whole topic worthy of its own discussion. This book does not cover the intricacies of CSS, but Wrox has a number of excellent CSS titles that can assist you on this topic.</p><p>Images and Assets The theme probably includes some image fi les and other creative assets or JavaScript fi les such as jQuery plugins. These assets are used in your theme to give your website a special look and feel — the look you want. How these fi les are structured in your theme is up to you; generally, they are placed in a subfolder of the theme’s main directory, such as img/, images/, assets/ or js/ depending on the fi le type. One of the nice things about themes being compartmentalized and packaged as they are is that the images can be referenced with relative paths from your CSS fi le.</p><p> c09.indd 214 12/6/12 1:21 AM Creating Your Own Theme ❘ 215</p><p>In addition, these creative assets can be referenced from your template fi les using built-in WordPress functions such as bloginfo('stylesheet_directory'). This keeps the theme very portable, if done properly.</p><p>Plugins As covered in Chapter 8, plugins contribute advanced functionality to a website. Some themes require specifi c plugins because the functionality is part of the theme’s personality, or they are needed to achieve a certain purpose in the theme. These plugins may be packaged with the theme or may require separate downloads. All plugins reside in the plugin folder. So while plugins are not directly part of the theme, they may be required to make the theme behave as the theme creator intended.</p><p>CREATING YOUR OWN THEME</p><p>Now you know how to install and activate a theme on your site as well as what the different aspects of the theme are. It’s time to take the next step and make your own theme. You can start a theme from scratch, but why not stand on the shoulders of giants and start with a theme that is similar to the look you want? Or, if you cannot fi nd one, start with a theme framework where most of the heavy lifting is done for you. There is no sense in reinventing the wheel, especially when you can use the power of open source software and start from working code.</p><p>Project Themes vs. Child Themes Before diving in, let’s talk about theme development strategies. Essentially, there are two classes of themes used in daily development. Which kind you choose to create depends on the amount of customization and the specifi c project you are producing. The goal of this section is not to show that one is better than the other; you, as the developer, will have to determine what meets your needs and goals. Let’s fi rst discuss project themes. These are one-off themes, often a modifi cation of an existing theme to meet the specifi c needs and design goals of a single project. These could be greenfi eld project themes built entirely from scratch or they could be a fork of an existing theme that you modify for the purpose of your project. In addition, there are starter themes that are designed to be the foundation for a project theme. Sometimes these themes are called bare-bones or naked themes. They intentionally have minimal styling and function as a blank slate with just enough to get you started. The reason to choose a project theme for your project is you have full fl exibility to edit the PHP and template fi les. However, in doing so, you lose the ability to update the starter theme without steamrolling your customizations. A second option is what is called a child theme. A child theme inherits from a parent theme. That means you get all the functionality and styling of the parent theme and then override certain aspects with your child theme. A child theme can take two different approaches. If you have found a theme that mostly fi ts your project’s needs and you can make cosmetic changes or minor functionality adjustments, then this is the way to go. Another approach is to use a theme framework. Theme frameworks are much like starter themes for project themes, except they are designed to be parent themes to your child theme. Theme frameworks </p><p> c09.indd 215 12/6/12 1:21 AM 216 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p> create the groundwork for your theme. You create a child theme of that theme framework with all your modifi cations. By using a child theme and a theme framework, you can make modifi cations to your child theme and retain the ability to update the framework as new revisions come out. You may have put it together already that technically there is a third approach. You can develop your own theme framework or parent theme to use in your own projects fi rst, and then use that to make your child theme for your specifi c project. The strategy you take really depends on your project’s goals and needs. To reiterate, every project is different and every developer is different. You will ultimately have to decide which method works for you and your project, and balance future maintainability with functionality requirements. In general, if you are modifying a theme, a child theme is the proper approach. However, in the real world (including but not limited to deadlines and budgets), project themes can offer the fl exibility you need. For the sake of going in-depth, you are going to explore a project theme in this chapter. With the basics of a project theme under your belt, you will be able to apply these principles to child themes as well. You will come back to child themes and theme frameworks at the end of this chapter for some more discussion.</p><p>What to Look for in a Starter Theme Sometimes it is easiest to fi nd a theme close to what you have in mind, use this as your starter theme, and modify it. At the minimum, you can add your own logo. Of course you have to pay special attention to the licensing on the theme. Conveniently, themes in the wordpress.org Theme Directory are all GPL themes, so you can modify and use them however you desire. Things to consider when starting from a working theme include: ➤ Licensing on the original theme ➤ Code quality ➤ How much modifi cation will be required ➤ Source artwork for the creative assets You will want to make sure that you are permitted to change the source theme you are starting with. You will also want to review the code quality of the theme because you will be the one making the modifi cations going forward. Does the theme accomplish the same presentation goals as your site, template-wise; does it convey your data the way you want it conveyed? There is no point in starting with a theme that you have to completely retool, if this is the case, you’d be better off starting with a theme framework and making a child theme. Does the theme have enough CSS hooks for you to style? Was search engine optimization (SEO) a consideration when the theme was developed? How much modifi cation will be needed to meet your requirements and will you be happy with the end result? Finally, does the theme come with source art, like the original Photoshop document, for you to modify? If not, do you need it, or will you be able to re-create any assets you must have? There are many considerations when developing a new project theme or modifi ed child theme for either yourself or a client. The convenience of modifying a pre-built theme is quite a temptation when you need to get a site up and running and out the door quickly. In practice, many sites have </p><p> c09.indd 216 12/6/12 1:21 AM Creating Your Own Theme: Getting Started ❘ 217</p><p> been built this way, where a client could select a stock template with a few minor modifi cations needed to quickly launch a new site. The catch is when a site goes beyond these simple modifi cations and you are stuck with modifying a poorly built theme. For that reason, even if a client likes a particular theme preview, you may fi nd it easier in the long run to rebuild a similar theme from scratch with a starter theme as the launching point.</p><p>CREATING YOUR OWN THEME: GETTING STARTED</p><p>Creating your own theme can be as simple or as complicated as you want it to be. Sometimes, you merely want to change a logo or a color and it is a basic process. Often, you are creating a theme from scratch to meet a certain need or condition, or solely to obtain a specifi c design look and feel. Whatever your motivations are, this section discusses the basics for getting a new theme and site design up quickly using the Twenty Eleven theme as a foundation. This example uses Twenty Eleven because it ships with WordPress, not because it is endorsed as the best choice for a starter theme. In practice, however, it has been used both as a starter theme and a parent theme for client projects. In the next several sections, you will tour the Twenty Eleven theme as an example of the elements of a working theme. You will also take this opportunity to modify the theme into your own version with your own customizations allowing you to leave the existing Twenty Eleven theme intact. It should also be mentioned that the Twenty Eleven theme uses HTML5 elements and an HTML5 shim. HTML5 is covered in more detail in Chapter 12, but just be forewarned that while using HTML5 is becoming more and more commonplace every day, it is still on the newer edge of web technology and all browsers may not support it.</p><p>Essential File: Style.css The style.css fi le is what WordPress uses to reference your theme, and this fi le is required for your theme to work. You could create a new theme with only a style sheet and index.php template fi le, although the index fi le can be empty. Using the power of WordPress’ theme hierarchy, WordPress automatically substitutes missing templates if your new theme does not have them. More on that later, but understand that a customized style sheet is what allows you to get started creating your own theme.</p><p>NOTE In practice, a style.css fi le is all you need to create a new theme. See the section, “Theme Hierarchy and Child Themes,” later in the chapter.</p><p>When creating your own styles.css for your new theme, the fi rst few lines are absolutely critical. These lines provide information to WordPress to use in the theme Control Panel and further reference your theme in the core. Your fi rst few lines should read as follows (substitute your information, of course):</p><p>/* THEME NAME: MyTheme THEME URI: http://www.mirmillo.com/mytheme/ DESCRIPTION: Theme for my new site. Based on Twenty Eleven.</p><p> c09.indd 217 12/6/12 1:21 AM 218 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p>VERSION: 1.0 AUTHOR: David Damstra (and friends) AUTHOR URI: http://mirmillo.com/author/ddamstra License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Tags: dark, light, white, black, gray, one-column, two-columns, left-sidebar, right-sidebar, fixed-width, flexible-width, custom-background, custom-colors, custom-header, custom-menu, editor-style, featured-image-header, featured-images, full-width-template, microformats, post-formats, rtl-language-support, sticky-post, theme-options, translation-ready */</p><p>The information here is pretty self-explanatory. There is an additional optional fi eld for theme hierarchy, covered later in the chapter. Make sure your theme name is unique to your installation. If you intend to release your theme for public use, either for free or for a premium, you should try to come up with a unique name to reduce naming collision in the directory and other installations. In addition, if you are deriving your theme from another theme, license permitting of course, you should uphold the license and copyright information from the original theme. Once you have addressed this required information for WordPress, the remainder of the fi le is traditional CSS and subject to the rules and structure imposed as such. To reiterate, you are making a copy of the Twenty Eleven theme and making it your own. This is not a child theme process. Child theme functionality will be covered later. In some cases, the workfl ow fi ts better if a new theme is created by copying and renaming the starter theme to a new folder and revising the style.css to refl ect the new project. This technique has pros and cons, but it works well for some teams because the foundation theme does not change often enough to warrant more complex methodologies. Plus, when you have a theme in production, you do not want a change to the parent theme to cause a cascading rendering issue in your successfully deployed site. Creating a copy and making a working theme in this new directory removes the dependency on future browser rendering testing, which is a time- and human-intensive procedure — that is, no one has automated this procedure yet. In the event that there is a substantial change to the parent theme, changes can be ported to the derivative themes on a case-by-case basis and tested as needed. Making a copy of the starter theme also enables you to create a hand-crafted CSS fi le by modifying the actual theme fi les rather than overriding the styles and carrying that additional byte baggage.</p><p>Moving forward, CSS rules are written out in the style.css fi le to turn your minimal layout into the professionally designed theme you are creating. Because you are working on your own copy of Twenty Eleven, a fork of it, so to speak, you can edit your own theme’s style sheet directly. CSS coding is outside the scope of this book and if done well, is an art and skill. Again, Wrox has several great books on working with CSS.</p><p>Showing Your Content: Index.php When creating your theme, you often have a chicken-and-egg problem. Maybe you are lucky and you know exactly what content is going to be published on your WordPress site, and exactly how it’s going to be structured. Maybe you even know exactly how the fi nal theme is going to look, or you’ve had a professional designer create some mock-ups for you. But odds are, your site is going to </p><p> c09.indd 218 12/6/12 1:21 AM Creating Your Own Theme: Getting Started ❘ 219</p><p> grow organically, and to see how the design, and therefore the style sheet, is going to play out, you need to have some content to display. You can use certain stock content fi les to import into your site and work through all the styles, or you can start building your site. Theme Unit Tests and stock content fi les are covered in Chapter 3.</p><p>The index.php fi le is the default template of your site. WordPress has a built-in decision engine that decides which type of information your visitor is requesting and then determines if there is a template fi le available for that information type. This hierarchy is covered later in the chapter, but the index.php template is the default, or template of last resort. If WordPress does not determine that there is a more specifi c template to use, index.php is it.</p><p>Usually, the index.php fi le contains your standard loop. This is a traditional blog format where the posts are displayed in reverse chronological order. For example, the following is some of the code from Twenty Eleven’s index fi le:</p><p><?php if ( have_posts() ) : ?> <?php twentyeleven_content_nav( 'nav-above' ); ?> <?php /* Start the Loop */ ?> <?php while ( have_posts() ) : the_post(); ?> <?php get_template_part( 'content', get_post_format() ); ?> <?php endwhile; ?> <?php twentyeleven_content_nav( 'nav-below' ); ?> <?php else : ?></p><p>As covered in Chapter 5, the loop is really the heart of WordPress. It is the most important concept to grasp because this is how your content is selected and ordered for publishing. You may look at the preceding code snippet and notice that the loop is really a small part of the code and that the content of the posts is not even being rendered in HTML.</p><p>And you would be correct. The preceding snippet uses the get_template_part() function, which was introduced in WordPress 3.0. This is a WordPress function that is very similar to PHP’s include() and require() functions, but it is more powerful because it is WordPress-specifi c. The get_template_part() function allows the theme developer to pull out specifi c code components for reuse, or for replacing in child themes. In the preceding example, WordPress is looking for a specifi c PHP fi le to include as the rendering of the posts in the HTML. This is actually a fairly complex example because it uses the get_post_format() function to fi ll in the second parameter of the include. Get_post_format() will do exactly what it says and return the format of the post. Post formats are covered a little later in this chapter. For the sake of this example, just know that if the post were marked as an image, this function would return image, or if it were published as a traditional post or marked as standard in the Add New Post Panel, it would return “false.”</p><p>The get_template_part() function will look for the template fi le in the current theme directory, fi rst as the specifi c version using the second parameter and then for the generic version ignoring the second parameter. That is, if you use the following code in your theme:</p><p><?php get_template_part( 'content', 'index' ); ?></p><p> c09.indd 219 12/6/12 1:21 AM 220 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p>WordPress will fi rst look for fi le named content-index.php in your theme folder and, if it cannot fi nd that fi le, will settle for content.php. If you are creating a child theme and neither of the preceding fi les is found, WordPress will continue the search in the parent theme folder. </p><p>In the Twenty Eleven example, the get_post_format() function is performing as a switch to pull in the desired content template for the appropriate post format. You will notice in the Twenty Eleven theme that there are many content templates — one for each post format type. This allows the theme developer to control how the different post formats are rendered in the browser. Another advantage of this tactic is breaking your code into smaller, manageable portions. You’ll fi nd this compartmentalization makes it easier to debug should you develop any problems with future changes, or need to add new functionality.</p><p>Showing Your Content in Diff erent Ways: Index.php The index.php fi le is really the most important template fi le in your theme. Although you cannot have an active theme in WordPress without styles.css — because that is how WordPress knows you have the theme available — index.php does the heavy lifting. In the early days of WordPress, the index template was the only template. The whole theme was just this one fi le, and it was really just the loop. That worked fi ne for WordPress when you used it as a traditional blog and this bloggy look is probably why WordPress is still derided as a blog engine. Hopefully you are reading this book because you know WordPress can be so much more, or if you did not know, that you are realizing it now. Your index template is very important; this cannot be stressed enough. It is the template of last resort that WordPress will use when it cannot fi nd a more specifi c one to use (see the section, “Template Hierarchy,” later in the chapter.) Nevertheless, your index fi le does not have to be a single loop showing your most recent posts. That is very traditional, and may work well for your site, but you can branch out. Your index fi le can be structured in so many different ways, it is truly limitless. It could contain multiple different loops from different tags or categories, or it could contain no loops at all. The index template could function as your error page, where you have more specifi c templates for every other piece of content in your site.</p><p>CREATING YOUR OWN THEME: DRY</p><p>As just discussed, these are the basics; WordPress requires a style.css fi le with properly formatted header information, and a theme must have an index.php template. Now you want to expand your theme to use more template fi les and capitalize on the robust theme engine found in WordPress. A good developer knows that you do not want to repeat code in multiple places; it is a bad design and gives your code one of those nasty smells. (You knew that right?) The code smell is called Don’t Repeat Yourself (DRY) and is, in fact, one of the easiest smells to get a whiff of and avoid. When you fi nd yourself tempted to cut and paste a code block from one template to another, that should be your fi rst whiff. Here is your opportunity to break out your templates into reusable parts. There </p><p> c09.indd 220 12/6/12 1:21 AM Creating Your Own Theme: DRY ❘ 221</p><p> are three obvious places where you can do this because you will reuse these components on nearly all pages on your site to give it that cohesive look and feel and structure. The header, the footer, and the sidebar information is essentially the same on all pages. You will also learn how to tweak these included fi les with additional logic to handle design exceptions.</p><p>Header.php You may think this fi le is a misnamed, but it is the standard name of this fi le that WordPress looks for. The header.php fi le includes everything at the top of your rendered page, up to the content area. This can be confusing because a properly formatted HTML document includes its own <head> information, which has its own special requirements. This header.php template fi le includes the HTML head, but it also includes the start of the HTML document and usually includes the site logo and navigation, assuming you are using an across-the-top horizontal navigation scheme. It can also include any additional elements at the top of your page, such as secondary navigation or a search area. Because this fi le includes so much more than the HTML header, the tendency is to take the printing term and call this area the nameplate, as in the nameplate of a newspaper or magazine. However, stick with tradition and leave the fi le name header.php in order to remain compatible with the built-in functionality of WordPress. When creating your header template fi le, a very important WordPress function must be included: wp_head(). This is a hook for WordPress to be able to attach certain functionality into your site header and is also used by plugins. </p><p>The wp_head() function is dropped in your HTML <head> node and is critical to the long-term compatibility and functionality of your theme so make sure this is included.</p><p>Now that you have broken out the nameplate section of your pages into the separate header.php template, if you were building a theme complete from scratch you would need to adjust your index.php fi le to include it. You could use the traditional PHP include or require family of functions, but the WordPress core functionality has a handy function to get around the theme paths. This is similar to the get_template_part() function discussed earlier, but there is a specifi c function for the header template. At the top of your index.php (and subsequent template fi les discussed later) simply add the following code:</p><p><?php get_header() ?></p><p>This function automatically includes the fi lename header.php from the current theme’s directory into the current fi le for rendering. This function does not have any additional functionality over a PHP include besides determining the correct include path for you, but it is much more readable when working on a theme.</p><p>Optionally, you could split out additional components from your header.php fi le and include them back in with PHP includes. Occasionally, if a site has a particularly long or complex global navigation, it is broken out for inclusion. In practice, working on smaller fi les is easier for editing because each template fi le has a specifi c function and reduces the complexity of debugging.</p><p> c09.indd 221 12/6/12 1:21 AM 222 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p>NOTE In a past life, one of the authors was called in to work on a web application where the entire application was created in a single fi le and the functionality was handled by triggering specifi c functions. Although the functions were nicely broken out, any time the application had to be debugged, the error messages were nearly meaningless. Although the line number would change (and skyrocket into the multiple thousands) they all occurred in index.php and inevitably the whole application had to be traced to determine what happened. Imagine how much easier it would be to troubleshoot a problem on the application if the error message indicated that the error took place in a 100-line navigation fi le, rather than a 10,000-line complete application fi le. Everyone writes bad code in their careers, and certainly we (the authors) are no exception, although none of us wrote this atrocity. What we are saying is: do yourself a favor and break code into smaller, manageable fi les whenever possible.</p><p>Footer.php In the same vein as the header.php fi le, everything below your content area should be separated out into a footer fi le. The nature of footer fi les has changed recently. Historically they have been the copyright and contact information, but in recent years, this real estate has been expanded to include additional navigation options and information relevant to your site. Most modern themes, including Twenty Eleven, include widget areas in the footer for customizable content. Widget areas are discussed later in this chapter. What you put in your footer is up to you, but because it remains by and large the same on every page, it is a prime candidate for breaking out into a separate include.</p><p>Again, make sure you incorporate the wp_foot() function into your footer template. This function allows WordPress to inject any necessary information from your active plugins and, as a rule, will include your </body></html> closing tags. Similar to the way your header template is included, WordPress offers the same functionality for your footer information with its own special include function. At the bottom of your template fi les, add the following code:</p><p><?php get_footer() ?> Sidebar.php Another candidate for breaking out is the sidebar, which is everything to the right or left of your content. Your sidebar could include the navigation of your site, if you have elected a vertical navigation scheme, or perhaps less important supporting information for your site content.</p><p> c09.indd 222 12/6/12 1:21 AM Creating Your Own Theme: DRY ❘ 223</p><p>The Twenty Eleven theme allows for either a right or left sidebar using the same sidebar.php fi le and places it with CSS. However, you could have multiple sidebar fi les for each column or specifi c sidebars for different pages. You need to consider a number of different issues when working with sidebars. You have to decide fi rst how many sidebars you are going to have. Second, you have to decide if they are going to be static sidebars, widgetized sidebars, or a hybrid. Finally, you have to determine how the HTML is structured so that you can make the CSS put the sidebars in the correct spots. Then you have to test in your target browsers and in all likelihood start over. Such is the life of the <a href="/tags/Web_developer/" rel="tag">web developer</a>. As mentioned, the Twenty Eleven theme’s stock sidebars are both the same fi le, whether on the right or left, and are fully widgetized. This enables you to sketch up a site with relative ease and use the WordPress Control Panel to place widgets as needed.</p><p>In your template fi les, place the following code to include the sidebar.php fi le:</p><p><?php get_sidebar(); ?></p><p>Sometimes having both sidebars in the same fi le does not pan out in the design, or more likely, the CSS. Or, you have broken out the sidebars into individual fi les for each sidebar location. For whatever reason, you can create two sidebar fi les for the traditional left and right places. For example,</p><p><?php get_sidebar('right'); ?></p><p> gets the fi le named sidebar-right.php, as indicated in the parameter of the function call, and includes it in the appropriate place. Again, this is similar to the get_template_part() function but is specifi cally designed for sidebars and is much more readable in the code. More advanced theme frameworks have multiple sidebars that deviate from the common notion that a sidebar is only vertical space on the left and right of your content. Some of these theme frameworks have what are essentially sidebars above, below, and even in the middle of the post loops. Having multiple widgetized areas like this transfers some power to the site administrator who can now place WordPress widgets all over the layout of the page. An important consideration when working with sidebars is keeping the balance between what portions are widgetized — meaning they can be controlled and managed by the content creator in the Control Panel — and what portions are hard-coded in PHP into the template fi le. Widgets can be very powerful, especially with many of the plugins that are available. But there are also cases where PHP code in the template fi le will get the job done and the content does not need to be updated by the administrator or is using built-in WordPress functionality to keep itself updated. Keeping this balance right is a developer decision.</p><p>Deviations from the Norm: Conditional Tags You have been a good developer and broken out all your repeating code snippets into their own templates or inclusion fi les. Good job, but the marketing director just called, and even though the </p><p> c09.indd 223 12/6/12 1:21 AM 224 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p> site is almost done, and he signed off on the design, he forgot to tell you that all the pages and posts in the Ponies category are supposed to have a pretty rainbow in the nameplate next to the site’s logo. Personal taste aside, this sucks, because you just made all the header.php fi les the same, and now only a handful of them need some special consideration. As with all things open source, there are many ways to handle this situation. You could probably handle such a simple example with some well-crafted CSS and the theme’s body class alone. Alternatively, you could create a whole category template fi le (discussed later) to style just this category. But because you’re dealing with only a tiny element, it seems like overkill to create a whole new template fi le. But wait — all is not lost. WordPress developers have had to deal with marketing directors before and knew this type of situation would come up eventually, which is why they included conditional tags. WordPress has many conditional tags built in, and covering each one is outside the scope of this book, not to mention particularly boring. But rest assured — these conditional tags exist and can address specifi c needs such as what type of page is being viewed, or the meta information about the content on the page.</p><p>To appease the marketing director, you might include something like this in the header.php fi le:</p><p><header id="branding" role="banner"> <hgroup> <h1 id="site-title"><span><a href="<?php echo esc_url( home_url( '/' ) ); ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a></span> </h1> </p><p><?php if (is_category('Ponies')) { ?> // overlay a pretty rainbow on the logo for the ponies category /img/rainbow.png" alt="OMG! Ponies! " /> <?php } ?> <h2 id="site-description"><?php bloginfo( 'description' ); ?></h2> </hgroup> ... </header></p><p>Now, any time the category of the content of the current page is in the Ponies category, your header also includes the rainbow.png. With PNG’s alpha transparency, it actually turns out nicely. This example only works for the category pages and not for the individual single posts pages in the Ponies category.</p><p>CREATING YOUR OWN THEME: CONTENT DISPLAY</p><p>A good theme enhances the content on your site. Not only is it visually appealing, suitable for the nature of the site, and brand appropriate, but the theme should also structure the content properly. WordPress has a variety of different templates and functionality to meet the needs of every site type. </p><p> c09.indd 224 12/6/12 1:21 AM Creating Your Own Theme: Content Display ❘ 225</p><p>The challenge here is to uncover the best combination of template fi les to include in order to achieve the optimal organization of your content. Not all themes need to have every template fi le type, and most do not; it is best to mix and match templates to meet your needs.</p><p>Customizing Your Homepage: Front-Page.php Homepage — who uses that term anymore? It sounds so 1990s, but what else should you call it? This section covers the fi rst page on your site when a visitor goes to your root URL. Apache users know that the index page of your site, the homepage if you will, is called “index.” It is usually called “default” on a Microsoft IIS server. The WordPress Control Panel refers to this as the front page; you can run with it for consistency’s sake.</p><p>A theme should always have an index.php fi le because after all else, index.php is the template of last resort. What if want your front page to have a special layout, perhaps one that features something about your site — product pages, for example? You do not want to mess with your index.php layout because you do not want to reinvent your entire theme just to accommodate this one special layout. Plugins and other tricks are available to handle this scenario; in fact, you can even use the WordPress Control Panel to set a static front page that is one of your existing published pages. But in this case, you are talking about a custom layout or HTML rendering, not a traditional page. The easiest way is to use the built-in template hierarchy and set a special front page by using a front-page.php template.</p><p>There are actually two template fi les that can function as your front page: front-page.php or home.php. In some older themes and even in the fi rst edition of this book, home.php was the only option. With WordPress 3.0, front-page.php became the preferred template fi le name for the front page. There is a little more involved here, depending on how you set your reading preferences in the Dashboard. Later in this chapter, you will see the template hierarchy and how these two templates actually rank. Creating a special layout, and therefore template fi le, for your front page is useful when your front page is unique. By and large, creating a unique front page is a marketing tool. Some reasons for creating a unique front page include: ➤ Featuring or showcasing a product or service ➤ Featuring or showcasing other portions of your website ➤ Driving traffi c to a certain portion of your site ➤ Explanatory steps of the processes involved with your product ➤ Delineating tiered levels of service that you provide Take a look at the basic example in Figure 9-1, where the front page is showcasing products or services that the website is marketing. These products would have their own supporting pages or posts in your site. Your front page has a nice image showcase front and center with links to the individual pages. You’ll use jQuery to enhance this showcase and rotate through the images. Alternatively, you could use a different JavaScript toolkit or Adobe Flash, but jQuery is already </p><p> c09.indd 225 12/6/12 1:21 AM 226 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p> included with WordPress and, frankly, it rocks, so why not use that. The bottom portion of the layout will include a recent news section.</p><p>FIGURE 9-1: A special template fi le can make your front page look unique.</p><p>If you cannot tell already, this is going to involve multiple loops. You will use the fi rst loop to create the content for the showcase. This loop will pull posts from a specifi c category or a custom post type. That way, the site admin can add and remove content from the showcase as needed, without visiting the code at all. Of course there will be certain design restrictions, such as image size and format and possibly certain conventions that must be followed in the post, but the capability to change this information in the WordPress Control Panel is a very powerful tool. The showcase loop, sometimes called slideshows, could look something like this (in this case, you are using custom post types, which are covered in Chapter 7):</p><p><div id="showcase"> <?php global $post; $args = array( 'post_type' =>'slides', 'numberposts' => -1, 'orderby' => 'rand' ); $slider_posts = get_posts($args);</p><p>// show showcase only if slides exist if($slider_posts) { foreach($slider_posts as $post) : setup_postdata($post); // get image $thumbnail = wp_get_attachment_image_src(get_post_thumbnail_id(),</p><p> c09.indd 226 12/6/12 1:21 AM Creating Your Own Theme: Content Display ❘ 227</p><p>'home-slide'); if ($thumbnail[1] == "600" && $thumbnail[2] == "160") { //checking thumbnail dimensions in css ?> <div id="feature-<?php echo $post->ID; ?>" class="slide"> <a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"> " title="<?php the_title(); ?>" /> </a> </div> <?php } ?> <?php endforeach; ?> <?php wp_reset_postdata(); ?> <?php } ?> </div></p><p>This creates an HTML rendering, as shown in Figure 9-1.</p><p>Take a look at what is happening here. The whole showcase loop is wrapped in a <div> with an ID of showcase. This is for the jQuery to hook onto later. In the PHP code, you are creating a custom query for the loop. The query is looking for custom post types called slides, which you would have previously established in the themes functions.php fi le and also set up in the WordPress Control Panel. This query is pulling all the slides from the custom post type and returning them in random order. The loop then proceeds to create <div> elements, each with a unique ID, again for jQuery and CSS hooks. The custom post type is set up to use the WordPress featured image as the slide content. This allows the site maintainer to upload the slide image that, in turn, links to a special landing page with more information in the post body. The preceding code snippet will only display the slide graphic if it is set to the specifi c dimensions, 600px wide by 160px tall in this case. Finally, countless jQuery plugins are available that can turn this now unwieldy block of content on your page into a very elegant slideshow.</p><p>The bottom section could be a traditional loop similar to the index.php template stock loop. The Twenty Eleven theme does not come with either a front page or home page template built in because, when using these templates, you are already on your way to creating a custom theme. Also, this is not the only way to get this functionality. As of WordPress 2.1, you can control what is shown on your front page and set it to any static page you have created and then create a special page template to accomplish the previously described design decisions. You can also build multiple loops — one using posts in a category for the slides, the other loop excluding the category for the news. This is how you used to do it. Again, how you choose to skin this cat is one of those choices you have to make as a developer as you balance the needs of your client with the ease of maintenance for your developers.</p><p>Show Your Older Posts by Date: Archive.php Eventually, if you are diligent, your site will have older content. And if you are really industrious you will be able to do those fun “One year ago on my site I told you about X” posts. Eventually, you may have copious amounts of content, so much that it is not feasible or appealing to show it all on the front page. That is, if content is being generated on a regular schedule, there will come a point in time when you will want to refer to something that is no longer on the front page or in the Recent Posts lists; this is the time when you need to delve into the vault of past content.</p><p> c09.indd 227 12/6/12 1:21 AM 228 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p>This is where the archive.php template steps in. You have many ways to present your older content. Harkening back to WordPress’ blogging origins, the most obvious method is to continue in reverse chronological order of your posts. If you do not have an archive template, WordPress simply uses your index template to show the older posts. The Twenty Eleven theme has an interesting take on the archive.php template that dates back to some of the original starter themes such as the Sandbox theme. This approach is very fl exible and creates date-based format visuals for the archives. Consider this code from the Twenty Eleven archive.php template:</p><p><h1 class="page-title"> <?php if ( is_day() ) : ?> <?php printf( __( 'Daily Archives: %s', 'twentyeleven' ), '<span>' . get_the_date() . '</span>' ); ?> <?php elseif ( is_month() ) : ?> <?php printf( __( 'Monthly Archives: %s', 'twentyeleven' ), '<span>' . get_the_date( _x( 'F Y', 'monthly archives date format', 'twentyeleven' ) ) . '</span>' ); ?> <?php elseif ( is_year() ) : ?> <?php printf( __( 'Yearly Archives: %s', 'twentyeleven' ), '<span>' . get_the_date( _x( 'Y', 'yearly archives date format', 'twentyeleven' ) ) . '</span>' ); ?> <?php else : ?> <?php _e( 'Blog Archives', 'twentyeleven' ); ?> <?php endif; ?> </h1></p><p>This code block shows how the theme’s archive template displays a unique header depending on whether the visitor is looking at the archived posts for a day, a month, or a whole year, or traversing by conventional pagination. Except for the fact that WordPress is inherently date-based, the specifi c archive template is not all that important. Although having the date information is useful when determining how recent certain information is, in reality, do you ever go back and look for posts published in May of 2007? More likely, you are looking for posts on a certain topic or fi led in a particular category or topic.</p><p>Showing Only One Category: Category.php Enter the category template. The category.php template creates a loop of posts from only a specifi c category. The category template is invoked when a visitor hits a specifi c URL with the category name in it. This could be something like http://example.com/category/zombies. In the category.php template, WordPress has already determined that your visitor is looking for posts in the particular category requests, so the default loop automatically makes this query for you, no special interaction required. When you use this template, you can generically display category posts and information, this is exactly how the Twenty Eleven theme is set up. For example, the Twenty Eleven theme places a header and category explanation information pulled from the category description, if it is available:</p><p><header class="page-header"> <h1 class="page-title"><?php</p><p> c09.indd 228 12/6/12 1:21 AM Creating Your Own Theme: Content Display ❘ 229</p><p> printf( __( 'Category Archives: %s', 'twentyeleven' ), '<span>' . single_cat_title( '', false ) . '</span>' ); ?> </h1> <?php $category_description = category_description(); if ( ! empty( $category_description ) ) echo apply_filters( 'category_archive_meta', '<div class="category-archive-meta">' . $category_description . '</div>' ); ?> </header></p><p>This covers the default category case, which is a nice default fallback template to have. But what if you want to make each category template have a unique look — for example, a color scheme or an icon? Assume your pony-and-rainbow fascinated marketing director now wants a Zombie category. Instead of using conditional tags, you can make a specifi c category template. Following the template hierarchy, WordPress will look to see if there exists a category template that is specifi c to the category requested in the URL. If you have not noticed yet, WordPress works from most specifi c to least specifi c until it fi nds the proper template. WordPress will select the most specifi c template for the type of information requested and work toward the more generic templates until it defaults to the index.php template. This is a critical aspect to learn when deciding on your theme templates, and it’s something you will review again.</p><p>For the marketing director, you can make a category-3.php template, for example, because the Zombies category has an ID of 3. The easiest way to fi nd a category ID number is to hover over the category name in the Edit Category Control Panel and look in the status bar at the bottom of the browser window, as shown in Figure 9-2.</p><p>FIGURE 9-2: Hover over the category name in the Control Panel to see the category ID in the status bar.</p><p> c09.indd 229 12/6/12 1:21 AM 230 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p>There is a little bit of a chicken-and-egg problem when you want to create a category template for a specifi c category. In order to name the template fi le correctly, you must create the category first to get the category ID. Lucky for you, there is another way. Since WordPress 2.9, users have been able to make category templates that use the slug. And the slug template fi le is preferred by WordPress over the ID-based one. So to avoid the chicken-and-egg problem, you can create the category fi rst and assign it a slug, in this case zombies. If you have this planned out ahead of time, you can make a category template fi le called category-zombies.php and be off to the races. These specifi c category templates work exactly the same as the generic category templates and pull the posts for that category automatically. Technically, it works the other way around: WordPress already knows which posts it is going to show you; it is just determining how to show them to you. What you are gaining with using a specifi c category template is the fl exibility to style each category individually. You have probably noticed by now that with WordPress there is always more than one way to do something. In the simple example of the marketing director, you can solve his problem with conditional tags or category-specifi c templates, or most likely, you can meet his requirements by using CSS because most themes have rich CSS hooks. But the extensibility of this feature is the killer aspect. Just knowing that WordPress has the feature built in will save you one day.</p><p>Show Posts of a Specifi c Tag: Tag.php The tag.php template functions nearly identically to the category.php template. It is invoked when a visitor requests a specifi c tag. This template is only benefi cial if you are actively tagging the content on your site. Most likely, you are assigning categories to content because that is a natural human organization structure, but tagging is not as clear-cut and often feels like an additional step. Nevertheless, if you are diligent in tagging content, a tag template is a nice addition to your layouts and can be benefi cial to cross-pollinate posts with related content. When this template is called, the loop automatically fi lls with posts of a particular tag for rendering. For a more in-depth look at how the loop actually works, refer to Chapter 5. Likewise, you can create a template for a specifi c tag. As with categories, you can use either the tag’s ID or slug to make the template fi le. If you want a special template for the Zombies tag, you use the slug of the tag to create a new template titled tag-zombies.php. You need to verify the tag’s slug on the Manage Tags Control Panel. Using the category and tag templates may not be the way you envisioned your content being viewed, especially if you are using WordPress more as a content management system. However, simply including these templates delivers free functionality and customizability from the WordPress core. These templates enable your visitors to explore your content in different ways and perhaps add a little stickiness to your site because your content is viewed in new and interesting ways. Categories and tags group related content and using these templates create an organic presentation for discovery of your site. Do not brush these templates off as simply reverse chronological listings of related content, such as an archives page. Envision creative ways to present your data; because you have visitors who are interested in at least some of your content, why not expose them to related items?</p><p> c09.indd 230 12/6/12 1:21 AM Creating Your Own Theme: Content Display ❘ 231</p><p>Other Archival Templates With that in mind, WordPress’ archival templates really bloomed recently. In addition to the templates discussed previously, you can also create special archive templates for custom taxonomies or custom post types. Although these templates fall in the archival template hierarchy, you can really think of any of these views as just groupings on a certain aspect of the content. If you have custom post types set up in your theme or content, as you did with the slides in the showcase example previously in this chapter, you can have custom archive page templates for those, too. This template fi le has a slightly different naming convention than the rest. For the slides, you would create a template fi le called archive-slides.php where slides is the custom post type’s name. Likewise, if you are employing custom taxonomies in your theme, you can make custom archive templates for both the taxonomy and the specifi c term. WordPress will choose the most specifi c template fi le it can, so the term templates will be chosen before the general taxonomy templates. Custom post types and custom taxonomies were covered in depth in Chapter 7.</p><p>How to Show a Single Post: Single.php You have set the bait with a great post headline, something witty and engaging. After the nibble, you set the hook with your excerpt of the post, and now you caught the visitor. He has clicked through to read the rest of the article.</p><p>The single.php template view is most likely the landing page on your site when a visitor arrives via a search engine. Assuming you have great content, the search engine will rank the explanatory page of your site higher than the index page, which only lists the excerpt. Therefore, it is best to invest some time in this template because it is very commonly viewed. Enhancing this template with related posts and other teaser content only increases the possibility of enticing a visitor to further explore your site, bookmark it, subscribe to your feeds, or best of all, link back to you. All of these events increase your search engine respectability.</p><p>You can display the full content of a single post with the single.php template fi le. WordPress has decided that the visitor has requested the full content of a single post; thus this template does not need to contain a loop, but simply a call to the the_post() function to get the data from the database. If you look at the Twenty Eleven theme, you will notice that the single.php template does, in fact, use a loop and the get_content_part() function to maintain consistency with the other templates, but because only one post is being shown, this is superfl uous. If you have a very long post, you can break it up among several pages by using the built-in WordPress functionality or special plugins. Internet users have very mixed feelings on this. Although general guidelines and studies have shown certain line lengths and content lengths improve readability, some vocal visitors detest the load time wait when paginating. This is a design choice based on your content type and site design. Adding links that are related to this post is a great way to entice visitors to explore your site more. Several plugins add related content to the bottom of a single post page or scan your content for keywords and links. In practice, you will have to try these out and see how they work with your site.</p><p> c09.indd 231 12/6/12 1:21 AM 232 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p>Alternatively, the poor man’s solution is to add a simple category or tag loop to grab some related- topic posts to the bottom of the page. It could be something as simple as this:</p><p><h2>Other posts in this category</h2> <ul id="related"> <?php $category = get_the_category(); $my_query = new WP_Query("category_name=".$category[0]->name." &showposts=5&orderby=rand"); while ($my_query->have_posts()) : $my_query->the_post(); echo '<li><a href="'. $post->permalink.'">"' . $post->post_title .'"</a> </li>'; endwhile; ?> </ul></p><p>Here you are taking fi ve random posts from the fi rst category of the current post. It’s not the most sophisticated method, but it is a simple way to show some related content links on the single post view. Another option is to show additional posts by the same author:</p><p><h2>Other posts by this author</h2> <ul id="related"> <?php $author = get_the_author_meta('id'); $my_query = new WP_Query("author=".$author&showposts=5&orderby=rand"); while ($my_query->have_posts()) : $my_query->the_post(); echo '<li><a href="'. $post->permalink.'">"' . $post->post_title .'"</a> </li>'; endwhile; ?> </ul></p><p>While having a single single.php template will suffi ce for most sites, WordPress does offer some more customizability for handling the individual custom post types. Using the slideshow showcase example from before, instead of rendering the clicked through landing page using single.php, you could create a special single-slide.php template. This template might leverage the featured image or other custom fi elds to make it more enticing or actionable.</p><p>Display a Page: Page.php When you’re using WordPress as a content management system, you have to make some decisions such as whether to use pages or posts. This is like cats or dogs — people have strong feelings about each. For the most part, this chapter has been talking about posts and custom post types. When working with a client, you generally create hybrid designs that use both pages and posts. Posts are used for temporal-based items, such as news and promotions, whereas pages are used for static information that does not change very often, such as products or services. Product pages are then augmented with related posts. This gives the client the benefi t of using the posts facets of the website to drive traffi c to the static product pages.</p><p> c09.indd 232 12/6/12 1:21 AM Creating Your Own Theme: Content Display ❘ 233</p><p>The page.php template works essentially the same as the single post template. There is no loop — unless you have created a special page template, but technically that is a different template fi le — only the call to the_post(). Yes, this is the same function as in single.php. WordPress considers the posts and pages to be fundamentally the same type of content and the_post() gathers the content from the database. As with previous examples, you can also have specifi c page templates for specifi c pages based on the ID of the page or the page’s slug. These follow the same pattern as before. In addition, you can also have custom page templates that you can assign to any page on your site, but you’ll cover this in more depth later in this chapter.</p><p>Display Post Attachments: Attachment.php To be honest, we (the authors) do not think we’ve ever used these template fi les in a production website intentionally. First introduced with WordPress 2.5, image.php was a special template just for showing — you guessed it — images from your gallery. Since then, this branch of the template hierarchy has grown and generalized to show any of the attachments you might add to a post based on MIME type. Many themes do not even have this set of templates; the Twenty Eleven theme includes an image.php but not an attachment.php template. In essence, this template works very similarly to single.php, so much so that single.php is the next default if this template does not exist in your theme.</p><p>The most common use for this template is to create an image.php template. This template provides a special template strictly for viewing your media gallery. A gallery can contain many different types of media; that is, it is not limited to images. This template will be called for any media item, unless there is a more specifi c match, and usually includes a description of the media and comment functionality. A great use of this template fi le would be for a portfolio site, such as a photography studio or another artistic collection. Again, this template functions nearly identically to the single post template, with slight variation to render an image rather than a paragraph. Similarly, template types can be used with media types other than images. You could create templates for video, audio, or applications; however, these would probably be very specifi c use cases, and in the wild you would rarely see or need these templates unless you were creating a specifi c niche website.</p><p>Template Hierarchy With all these template fi les to choose from, how does WordPress decide which one to use? The WordPress core is pretty smart in this regard. Based on the URL, WordPress determines what type of content is being requested and can make a starting determination. Then WordPress works out the specifi city of the template to be used, using the most specifi c template that matches the criteria fi rst, and falling back to more general templates until it fi nds a match. This system works well, in that it is fault tolerant by always cascading back to index.php but extremely powerful because, as the developer, you can make custom templates for very specifi c situations if needed. This is best illustrated with the fl owchart in Figure 9-3, adapted from the WordPress Codex. There is a more complete version online.</p><p> c09.indd 233 12/6/12 1:21 AM 234 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p>Visitor Request</p><p> is_search() is_front_page() is_page() is_404() is_attachment() is_archive()</p><p> is_author() is_category() is_tag() tag-{slug}.php front_page.php category-{slug}.php author-{nicename}.php page-{slug}.php {custom}.php tag-{id}.php author-{id}.php category-{id}.php single-post.php attachment.php {mimetype}.php author.php tag.php page-{id}.php single-{posttype}.php category.php</p><p> search.php home.php pagpe.php 404.php single.php archive.php</p><p> index.php FIGURE 9-3: The WordPress template hierarchy</p><p>As you can see, there is a nice decision tree happening here, and the fl exibility is very powerful. Not all themes have or need all template fi les. But certain more customized or special use case themes can capitalize on this hierarchy and create a unique application of WordPress.</p><p>It’s also worth mentioning that the search template hierarchy is defi ned in template-loader.php, where a hook is defi ned before the search tree is started — template_redirect — that lets you change the template selection process. This is used mostly for handling URL redirections, like a “shortlink” defi ned for a page, which might hide some of the URL information normally used to decipher what templates WordPress applies.</p><p>WARNING On some sites we have built, there have been occasions where categories, tags, pages, and even authors have had the same or similar taxonomies. For example, on a corporate news site, you may have a page about a department and a department category for news about that department, and some information may be tagged by the department name. In cases like these, the WordPress template decision tree can get confused and make unintended choices. You can work around this in several ways: either by carefully crafting your taxonomy, by ensuring each slug is suffi ciently unique to avoid collisions, or by enforcing your desired behavior via the .htaccess fi le. The crux of the issue here is how WordPress handles permalinks, which boils down to pattern matching on the slug metadata.</p><p> c09.indd 234 12/6/12 1:21 AM Creating Your Own Theme: Additional Files ❘ 235</p><p>CREATING YOUR OWN THEME: ADDITIONAL FILES</p><p>It is tough to sort out the various template fi les into which ones are critical, which are essential, and which are just nice to have. Each theme’s template fi le collection will be different and tailored to match the content or design goals of the author. The truly critical and essential templates have already been covered. In some circles, many of the following templates would fall into the categories already discussed, so you will have to make your own decisions here. Do not think that because these templates are being covered later, they are any less important than any other template. Consider each template type a tool, and how you use the tools is what truly matters.</p><p>Handle 404 Errors: 404.php A 404 page is a fact of life. Eventually, your visitors will fi nd something that went stale. In contrast to a traditional website, WordPress really helps you avoid them because typically all the navigation items are dynamically created by content that actually exists. But it is still possible that your visitor will fi nd a link that is no longer around, so your 404 page comes up. The Twenty Eleven theme provides a really good practice with the stock 404 template by including a search box, recent posts, most used categories, and a tag cloud. This way, visitors who stumble across this page have an opportunity to fi nd what they are looking for. Other good practices include showing a list of possibly related content, in the form of “I couldn’t fi nd what you asked for, but maybe one of these posts would interest you.” You do not want a 404 page to be a dead end; always offer something else to view and a way out. At our shop, we trigger a developer e-mail or Twitter warning to let you know someone asked for a lost URL. Especially if there is a referrer in the HTTP headers, you can track down where the broken link originated. At the least you know something went wrong and can do some research. Also, your 404 page should be funny. Humor is good medicine and it is nice to disarm visitors who might be upset that what they were looking for is not there. It is good practice to expose errors to your developers but show something useful and meaningful to your site visitor. Think back to the days of the Twitter fail whale. As Twitter was growing, it often had scalability issues and the fail whale was seen more often than not. But by keeping the error message lighthearted, the Twitter fail whale has quickly emerged as an Internet icon and garnered its own cult following. Although not strictly a template fi le, another error to hide from your visitors is a database connection error. The default database connection error is ugly and exposes a little too much information to your visitor, who hopefully is a good guy and not going to use that information against your website. WordPress introduced a new function in version 2.5 and later back-ported it to previous versions where, if the database connection fails, WordPress looks for a db-error.php fi le in your wp-content directory.</p><p>NOTE This fi le resides outside of your theme directory. Because there is no database connection, WordPress does not know what theme to display.</p><p> c09.indd 235 12/6/12 1:21 AM 236 ❘ CHAPTER 9 THEME DEVELOPMENT</p><p>You can put whatever code and CSS in the db-error.php template you want, except dynamic data or WordPress functions, because they will not work without the database. This is another situation where we place a stock db-error.php in all of our WordPress sites, with a generic but friendly error message and then notify the development team that an error has occurred.</p><p>The following is a sample db-error.php fi le:</p><p><?php //error_reporting('E_ERROR'); mail('developers@mysite.com','WP SQL Connection Issue on '.$_SERVER['HTTP_HOST'], 'This is an automated message from the wordpress custom db error message file.'); ?> <html> <head> <title>Temporarily Unavailable

    is Temporarily Unavailable

    The webmaster has been alerted. Please try again later.

    In the rare occurrence that WordPress cannot connect to the MySQL database, rather than showing an ugly database error, site visitors get a friendlier error message and the developers receive an e-mail. This also informs the visitors that no further action is required on their part, besides checking back later, because the error occurred on the web-hosting server and not on their side. This acknowledgment removes confusion or uncertainty on the visitor’s side. The caveat to this is that when things go really wrong, beyond just a hiccup, the developers can get fl ooded with e-mails.

    Author.php Earlier, you were grouping historical content by category or tag; you can similarly use the author.php template to group all of an author’s content into one view. In addition, you can create specifi c author

    c09.indd 236 12/6/12 1:21 AM Creating Your Own Theme: Additional Files ❘ 237

    templates either by the author’s ID or by the author’s nicename, which is the author’s username. As before, the human readable nicename template is preferred by WordPress over the ID-based one. Sometimes your site has multiple authors, such as your development team site at work. In cases like these, a visitor may want to fi nd additional articles posted by the same individual. The author.php template fi le shows only posts written by a specifi c author. The author template behaves just like a category or tag loop. One nice feature of the Twenty Eleven theme is that this template also includes any author information that the author chose to submit in the admin Control Panel.

    If the author submitted some biographical data, that information is published here. This functionality could be enhanced if the profi le page had a rich text editor for the biographical information, and possibly some expanded custom fi elds. In production sites, these fi elds have been used to create a multi-business partner site where each author was, in effect, a company. You can also create a Rolodex-type site using this method.

    Comments.php The comments template used to be one of the more complex templates. This template fi le handles both the comment loop, including trackbacks and pings, and the input form for a visitor to submit the comment in both logged-in and logged-out cases. Although these tasks are functionally related, sorting through this template fi le, you’ll see a lot of “if . . . else” conditionals that make it diffi cult to theme. Your theme may not even include comments, especially if you are using WordPress as a CMS, but if it does, you can include the comments functionality templates in your other templates with the following code:

    Countless variations on the comments theme exist for the look and feel — way too many to discuss the merits of any in particular. One thing to consider when working on this template fi le is the

    c09.indd 237 12/6/12 1:21 AM 238 ❘ CHAPTER 9 THEME DEVELOPMENT

    threaded comments functionality introduced in WordPress 2.7. See the WordPress Codex for more information about using wp_list_comments(). It should also be noted that in WordPress 2.7, the comments loop was simplifi ed to look more like a traditional post loop in the code. In addition, many new functions have been introduced to make the comment templates much more straightforward, and the Twenty Eleven theme does a great job of utilizing these functions. The Twenty Eleven comment template is a good place to start if you are looking at overhauling the comment section of your site.

    One particular improvement is the comment_form() function. This function now handles the rendering of the actual comment form in the comment template. Prior to this function, all of the form and form logic was handled in the template itself, which added extensively to the complexity. This function was introduced in WordPress 3.0 and is pretty extensible if you need to customize your comment form. As mentioned, the comments template can be confusing and is a whole subtheme unto itself because there is so much going on. Some partial theme websites have popped up that simply sell comments themes — for example, http://commentbits.com/. Not a whole site theme, but just the special templates for comments with a few variations. This could be a simple way to get a stylized comment subsystem up and running quickly.

    Adding Functionality to Your Templates: Functions.php The functions.php template is not a display template, so it is not like the other templates you have covered, but it is a very important fi le even though it does not directly display content on your website. Chiefl y, the functions.php fi le is where the special sauce that makes your theme tick goes. It is the place where you can put what has traditionally been called “library code.” If in your templates you fi nd repeating code or need some special functionality, this is where it can go. WordPress automatically includes this fi le during execution so the functions are available in all of your template fi les.

    Often when you are adding functionality you have to decide if the code belongs in functions.php or in a plugin. A general rule of thumb is that if you are adding something that is confi gurable, as in the user may want to disable it without affecting the look and feel of the site, it belongs in a plugin. If you are including something that is essential to the theme and is always on, it belongs in functions.php.

    The stock functions.php theme is very well commented. Each function and logic block of a function has a one-line comment explaining what it does. This makes functions.php easy to modify and extend. However, the majority of these functions will not need to be modifi ed in your production site, unless you have very specifi c needs. Most of them simply add to the template fi les to create a hook-rich HTML template for your CSS styling skills.

    One thing that is important to consider when adding on to your theme’s functions.php fi le is whether the functionality you are adding really belongs compartmentalized by theme or whether it is standalone functionality that belongs in a plugin. This is about whether the code you are adding is directly applicable to the theme or if it is portable and can be used no matter what theme the site is running. This can often be a diffi cult choice, especially when you realize that you use the same hooks and fi lters discussed in Chapter 8. In essence, the functions fi le is a library of plugins all nestled into one fi le.

    c09.indd 238 12/6/12 1:21 AM Creating Your Own Theme: Additional Files ❘ 239

    One of the main purposes of the functions.php fi le is to enable or disable certain WordPress features for your theme. In the Twenty Eleven theme, this is all done during the twentyeleven_ setup() function. You will notice it enables support for various features including post-formats, custom background, post-thumbnails, and a few others. These are all features of WordPress that the Twenty Eleven theme uses; therefore, they must be enabled. Think of this as feature fl ags. How each feature is enabled and confi gured is dependent on the specifi c feature. For example, enabling post- thumbnails or featured images is as simple as this:

    // This theme uses Featured Images (also known as post thumbnails) for // per-post/per-page Custom Header images add_theme_support( 'post-thumbnails' );

    Some feature fl ags can take, or may require, confi guration information, such as:

    // Add support for a variety of post formats add_theme_support( 'post-formats', array( 'aside', 'link', 'gallery', 'status', 'quote', 'image' ) );

    However each feature is different and will need to be enabled and confi gured based on your theme’s needs. In addition, the functions fi le establishes and identifi es your navigation menus. In your functions fi le, you introduce how many menus your theme will have and assign them names. Later in this chapter, You will learn how to place menu locations in your template fi les and assign menus to these locations in the WordPress Control Panel.

    Similar to menus, you also identify and create widget areas in your functions.php fi le. Generally, widget areas take more confi guration than menus do. Like menus, you register widget areas, or sidebars, in the functions fi le and later in this chapter you will learn how to place these locations in your template fi les. This fi le is for creating your own behavior and functionality for your theme. You could be introducing new presentation logic or new features that are specifi c to your needs and goals. But, also you can override or augment existing WordPress features — for example, in the Twenty Eleven theme:

    function twentyeleven_body_classes( $classes ) { if ( function_exists( 'is_multi_author' ) && ! is_multi_author() ) $classes[] = 'single-author'; if ( is_singular() && ! is_home() && ! is_page_template( 'showcase.php' ) && ! is_page_template( 'sidebar-page.php' ) ) $classes[] = 'singular'; return $classes; } add_filter( 'body_class', 'twentyeleven_body_classes' );

    This functions adds on to the body_class() function that assigns CSS classes to the body HTML node. The default body_class() function returns many useful classes that you can hook into with your CSS styling, but sometimes you need something special. In the case of the Twenty Eleven theme, the preceding function appends classes to the array that is returned based on certain criteria.

    c09.indd 239 12/6/12 1:21 AM 240 ❘ CHAPTER 9 THEME DEVELOPMENT

    When modifying the starter theme functions, you have to make a choice. Sometimes, if you are making minimal changes to the fi le, you can just modify the fi le directly, accepting that this will break any upgradeability of the theme. Alternatively, you can include your own custom_functions.php from the functions.php fi le and make all your own custom changes here. The caveat is that if you overwrite the functions.php fi le, either through a theme update or other user error, you have to remember to put the include back in that fi le before your head gets too bloody from beating it against the wall wondering why your custom functions are not running. In practice, both of these scenarios have been used successfully. The amount of power and control available to you in the functions fi le can be staggering and this power can quickly grow your fi le size out of control. For example, some of the more advanced themes and the premium theme frameworks include their own control panels to modify theme settings. These other theme frameworks are covered later in this chapter. Theme control panel code resides in the functions.php fi le. For example, consider the popular Thematic theme framework. To keep things manageable and distinct, the Thematic functions.php is simply a list of includes of other function fi les. This logically breaks up and separates the different facets of the theme framework and keeps the fi les from becoming unwieldy. This theme also includes a basic control panel to control some of the theme settings. To create your own theme control panel, you have to register your theme control panel functions with WordPress. In addition, you have to create the HTML form for your control panel within your functions.php fi le. This is one of the reasons we like the way Thematic has broken up the fi les into separate areas of concern; the mixing of PHP and HTML code never turns out pretty or readable. It is best to keep this information separate and in maintainable fi le sizes. Creating your own theme control panel is outside the scope of this book, but it is defi nitely a great feature to have and most of the premium theme frameworks include this functionality. Having a theme control panel helps your WordPress theme bridge that gap from blogging engine to full- fl edged content management platform for the average user. However, the new Theme Customizer, released in WordPress 3.4 and covered later in this chapter, may work for many sites as a simpler theme control panel. For a coder, WordPress is easily extendible through the code and the vast WordPress functionality, but for the average user, who is probably your client, avoiding code is crucial making these control panels and theme customizers ideal for your end user.

    Search.php The search template fi le is really a misnomer. This template is actually the search engine result page (SERP). The search form itself is called searchform.php and is covered in the next section. The concept of a search engine result page is pretty self-explanatory. It is going to show the results of what the visitor looked for, by default in reverse chronological order. Chapter 11 covers some of the weaknesses with the built-in search functionality of WordPress and addresses some alternatives to enhance the user experience. This is all that the basic Twenty Eleven search template does. If there are results, this template presents them to the browser, but if not, it shows a new search form. You can do a couple of things to your search engine result page to improve on the default. First, you do not want your results page to be a dead end if there are not any search results. Plugins are

    c09.indd 240 12/6/12 1:21 AM Creating Your Own Theme: Additional Files ❘ 241

    available to offer related searches or spelling variation searches based on what was initially entered. This will make the search itself behave more like a traditional search engine. Still, if you do not have any search results, offer up some alternative content that the visitor might be interested in, similar to the way in which the default Twenty Eleven 404 template behaves. This might be a good place for a tag cloud or a list of your most popular content. Plugins are available for showcasing your most popular content or you could create a custom query, but you would have to decide what your metrics are. For some sites, the top content was essentially a known issue — that is, we decided what the top content would be. In this case we created a special post category and made a new loop to show only this category in the SERP page. If you do have results, some people like to see the search terms highlighted in the search engine results page. The Twenty Eleven theme uses the_excerpt() to display the content excerpt in the results. This is where you will make some changes to highlight the search terms. The downside of having your theme split up into many template fi les is that you have to chase the include() and the get_template_part() functions to fi nd the correct fi le to edit. In the case of Twenty Eleven, the get_template_part() is looking for the content template for the proper fi le type. For the sake of brevity, you can chase this back to content.php for general post content. In the content.php template, there is an “if” statement around line 35 checking if the content is being displayed on a search engine results page or not. This is where the theme decides whether it’s showing all of the content or just an excerpt, so you can modify the way excerpts are displayed. Replace this line,

    with the following:

    \0 ',$excerpt); echo $excerpt; ?>

    Because the_excerpt() echoes the content directly to the rendering, you have to use the plugin API function get_the_excerpt(), which returns a string instead. Run this string through the regular expression replace to put span elements around all the search terms and then echo this out to the rendering. In your CSS, you can add a nice rule to highlight these span elements to match your theme. Finally, if your visitors did not fi nd what they were looking for after reviewing the search results, rather than forcing them to scroll back up to the top, you can provide a second search form at the bottom to refi ne their search. After the results loop, add something like the following:

    Not seeing what you're looking for? Try again

    c09.indd 241 12/6/12 1:21 AM 242 ❘ CHAPTER 9 THEME DEVELOPMENT

    The Twenty Eleven theme has the search form already enabled for you. Chapter 12 discusses improving the way search works through plugins and some alternatives.

    SearchForm.php The generic search form is pulled from the WordPress core template fi les and is pretty basic looking. In cases where your theme needs a customized search input fi eld, create a new template named searchform.php. This form can then be styled to match the rest of your theme. The search widget automatically uses this template to include this form in your regular templates with the following code:

    The basic Twenty Eleven search form looks like this:

    Note that because this same form could be used in the unordered lists of the sidebar as well as wherever else you may include it, the HTML markup may need to be adjusted to be generic. Another option is for special-case search forms, often seen in the nameplates of sites, is to create a traditional PHP include for the search form. Make sure the fi lename is not one of the reserved fi lenames for the template engine, and then include it in the appropriate place in your other template:

    Remember to use the bloginfo[] array to keep the theme portable. You can also use this method to comply with the DRY principle when there are consistent elements across multiple pages but outside of the header and footer templates. WordPress itself does an excellent job of enforcing DRY through the variety of page templates, assuming you, as a developer, stick to the rules. But there are always more ways to skin a cat and traditional PHP operations can help out here. This functionality is often used to keep the template sizes manageable.

    Other Files Here are some other fi les that polish off your theme. For the Manage Themes Control Panel, you will want to include a screenshot for easy visual recognition of your theme. Create an image fi le to represent your theme that is 300px wide by 225px tall and save it as a PNG. GIF and JPG are also accepted and preferred in that order. Traditionally, this image is an actual screenshot of your site using your theme. The remainder of the information for each theme on the Manage Themes page comes from your style.css header information.

    c09.indd 242 12/6/12 1:21 AM Custom Page Templates ❘ 243

    Many themes include several language fi les and are ready out of the box for localization. If you intend to launch your site in multiple languages, pay attention to the special considerations involved. Localization and internationalization are well outside the scope of this book. Just bear in mind that the WordPress supports this functionality when you need it.

    CUSTOM PAGE TEMPLATES

    Occasionally, you will have a specifi c page that requires a unique layout, relative to the rest of your website. This could be a contact page, or it could be that each product on a brochure website has its own specifi c page. It could be that you are making a custom landing page for a marketing campaign or a QR code. Maybe using a general page.php template is not going to meet the needs of your site because each page has its own distinctive qualities. Possibly, you have widgets you would like to display on certain pages and not others, although you could probably accomplish this with a plugin like Widget Logic. Or perhaps you are integrating a third-party web application into your WordPress site. This is where page templates step in. You can assign page templates to a page using the Write panel in the Administration Control Panel. WordPress will assign which page template to use when displaying your content following the already established specifi city pattern. For example, if your page is assigned a page template, that will be selected because a page template is the most specifi c option. If the default page template is set, the traditional page.php template discussed earlier will be used to render your content. Finally, if neither of those templates is available, WordPress will use your index.php template. In Figure 9-4, you can see several page templates to choose from, including the default page template, Twenty Eleven’s templates for a showcase page and sidebar page, discussed later, and the two added as FIGURE 9-4: Selecting the examples, Boring and Fancy. page template

    When to Use Custom Page Templates Many reasons exist for having custom page templates in your site. Custom page templates are very powerful tools to add to your arsenal, and when used effectively they can extend the breadth of your site immensely. Custom page templates are yet another way to assign templates to specifi c pages. Unlike previous examples, which relied on an inherent attribute of a page, such as the page slug or ID, category, or tag, custom page templates can be assigned arbitrarily through the Write Panel to any page in your site. A simple example is to create page templates for unique product pages, where the sidebar of each product page has unique data and links specifi c to that product. As with everything in WordPress, there are many ways to achieve this functionality, but sometimes all that’s needed when creating custom page templates is a simple, straightforward method. And often, simplest is best. Another simple example is to create a custom page template that uses an iFrame HTML element to include a third-party web application. Depending on the exact needs and aspirations of the site (not to mention budget), this can be a quick and dirty way to integrate two sites into one. The caveats to

    c09.indd 243 12/6/12 1:21 AM 244 ❘ CHAPTER 9 THEME DEVELOPMENT

    this method are the same as you would usually fi nd when using iFrames, which are bookmarking and competing look and feels. But admittedly, this method has been used before because sometimes the quick and dirty method is all that you need. More complex examples include integrating different web applications into your WordPress site. For example, a page template could be used to create a custom order page that posts directly back into your e-commerce package. This would be a nightmare to set up and maintain inside the WordPress Control Panel, but when using custom page templates, it is all in the code, and you still get the gooey goodness of WordPress to wrap it all in. In real life, custom page templates are used for event calendaring and registration. On one occasion, an expansive education class was built offering web applications for searching and displaying courses as well as registering for attendance either in person or via the web. This system had been in place for several years and was heavily used. The simplest way to integrate this education registration system into WordPress client sites was to create custom page templates. In essence, this extended the existing registration system with some REST web service commands. Then a set of custom page templates was created that communicated with the web services but displayed the contents locally inside the WordPress site wrapper, using the local style sheet. Although setting up the page templates was daunting at fi rst, the benefi ts in the end were enormous: ➤ Continued use of the existing system that corporate staff was already knowledgeable about and trained to use. ➤ Extended registration options to multiple sites, therefore increasing the potential audience. ➤ Even though the registrations were spread across multiple web properties, they were still centralized into the one system. ➤ The education system matched the look and feel of the local website because it utilized the local theme of the WordPress site. How to Use Custom Page Templates Creating the custom page templates themselves is really easy. The goal of the templates and making the templates achieve the goals is what really complicates the matter. To create a page template, copy an existing template that is similar to the new template you are going to make; usually this is the page.php template. Name this new template fi le whatever you want and keep it in your theme directory. However, in our development shops, we tend to follow a convention that page templates are named t_templatename.php. That is, they are prefi xed with thet_ so it is easy to distinguish between traditional template fi les and individual page templates, although the name of this fi le really does not matter as long as you avoid the reserved fi lenames in the hierarchy. To make your new template a page template, you must include a special comment section at the top of the fi le:

    c09.indd 244 12/6/12 1:21 AM Custom Page Templates ❘ 245

    This must be in the fi rst couple of lines of your fi le for WordPress to scan and register as a page template. In practice, the only thing above this stanza is the source code control comment. The name of your template can be anything you want. It should be meaningful, but not too long, because WordPress will use this PHP comment to populate the drop-down box in the Control Panel. Your page template is now registered with WordPress. The remainder of your page template can be whatever you need to accomplish your page template goals. You can, and most likely should, use the built-in WordPress functions such as get_header() and get_footer() as well as the content gatherers. Basically you can do whatever you need to do here; just remember you will have to sleep in the bed that you make. For example, if you remove the dynamic WordPress sidebar generation and replace it with static HTML, you have also removed all the functionality from the Control Panel to manage widgets on this page template. It would be a better practice to register a new widget area on this page template and continue to use the Control Panel to manage this content. Keep in mind that page templates are not restricted to displaying page information. You could create a page template that displays a traditional post loop or do something that is completely unrelated to the WordPress content. Then just leave the page text editor blank, or use it to write instruction notes to yourself.

    Stock Twenty Eleven Page Templates The Twenty Eleven theme comes standard with two page templates for use on your site. You will look at those briefl y here, since you have them available.

    The fi rst page template that comes with the Twenty Eleven theme is sidebar-page.php. This template is pretty simple and straightforward. Basically, it adds a sidebar to your page template. Pretty self-explanatory.

    The second custom page template is more complicated. It is called showcase.php. The showcase page template is designed to be a fancier index page for your WordPress site. It has a couple of features built in. First, it has a highlighted content area. This content area appears directly below the navigation on Twenty Eleven. This content comes directly from the content of the page that you assigned this template to. Second, it has a showcase for posts. This is similar to what was done earlier in this chapter for the slides and slideshows, but for Twenty Eleven, it uses actual post content. To feature posts, you need to set them as sticky posts. The nice thing about this method is the features can be mixed format, you can have images, text, or images and text in the featured area. Once you have this page published, set it as your front page in the Reading Settings Control Panel and you will have a fancy new index page. Custom page templates are very powerful tools. Truly, if you cannot fi t your content into the predefi ned template types, you always have this last trick up your sleeve to make a custom page template and override everything. This is also a great way to add special non-WordPress functionality to your website.

    c09.indd 245 12/6/12 1:21 AM 246 ❘ CHAPTER 9 THEME DEVELOPMENT

    OTHER THEME ENHANCEMENTS

    These are some additional enhancements that you can make to your theme. Most of the following ends up in your functions fi le and not as individual fi les. These are enhancements to your theme that enrich the site administrator’s CMS capabilities.

    Menu Management As mentioned before, most WordPress sites that are being used as CMS capacity have a mixed bag of pages and posts. The most diffi cult part of the hybrid pages and posts layout is creating a meaningful navigation. Your site’s global navigation is a very important aspect of your site if you intend to have any stickiness with your visitors. Visitors should be able to explore your content organically and experimentally through related posts and pages, but there should also be a strategy to your content organization and this is the function of your global navigation. On occasion, we have lucked out on the structure of a site and have been able to create two tiers of navigation, one for the page content and one for the post content. Before WordPress 3.0, the two content types essentially needed to be intertwined and the navigation had to be hand-coded using the wp_list_pages() functions with many different parameters to get the menus you wanted. This was all coded in the templates and not very fl exible for the site administrator. However, those days are gone. With WordPress 3.0, a new Menu Management system has been introduced. This menu management gives all the control to the site administrator. As a theme developer, you just have to set it up for your theme presentation.

    First, in your functions.php template, you must enable the menu feature. This is done with:

    if (function_exists('add_theme_support')) { add_theme_support('menus'); }

    The next step is to register the menus for your theme. Basically this means assigning named locations for each menu to earmark the HTML real estate for the menu. You will use the register_nav_menu() function for this. This function takes two parameters. The fi rst one is a nickname or handle that you will use in the template fi les. The second parameter is a friendly human-readable name that is used in the WordPress Control Panel. For example, the following will create a single global navigation menu for use in your theme:

    register_nav_menu('primary', 'Global navigation menu');

    You can actually have as many menu locations in your theme as you want or need. Just feed the register_nav_menu() function an array of locations, like so:

    register_nav_menus( array( 'primary' => __( 'Primary Navigation', 'twentyten' ), 'supernav' => __( 'Super Navigation', 'twentyten' ), ) );

    This will notify WordPress that you have menu locations available and identifi ed, but you also need to assign their position in the template fi les. Usually, because of the way websites are designed, this

    c09.indd 246 12/6/12 1:21 AM Other Theme Enhancements ❘ 247

    occurs in your header.php template. To place the desired menu in your template fi le use the wp_nav_menu() function like so:

    'primary' ) ); ?>

    This function can take many parameters, passed in as an array, to control the HTML styling, but the important parameter is to identify which menu you want placed at this location. In the preceding example, the real estate is allocated for the menu nicknamed primary. Finally, to glue this all together, your site administrator can use the Menus Control Panel to manage the menu. Notice you are identifying a menu location, and then assigning that location to an actual position in the HTML hierarchy. This is all at the code level. In turn, the site administrator assigns content to these named locations through the Control Panel. So while they all must be set up together so that they work together, they are actually disconnected. Assigning meaningful names to the menu locations is important for the Control Panel aspect of menu management, but how the site administrator uses that menu may be different than you intend. What is meant by “disconnected”? Let’s go back in time to how WordPress used to build menus before the new menu system. This process involved using the wp_list_pages() function with many parameters to hand-craft the exact menu you needed. In fact, the fi rst edition of this book devoted many pages to reviewing this topic. Creating the menus programmatically in the template fi le worked because it was directly tied to actual content of the site and the hierarchy of pages. However, it was not perfect. You had to use tricks such as building a blank page to show up in the automated navigation menu, and then use the Page Links To plugin by Mark Jaquith to redirect that page to category or archive templates. In addition, the PageMash plugin was recommended to manage the page hierarchy and page order for the wp_list_pages() function. This tried and true method is frequently employed for many sites. It works because it is programmatic, meaning it is predictable. It works because it is directly tied to content. It sometimes does not work when the content gets a major change.

    Sometimes, a change in the content means the wp_list_pages() parameters need to be adjusted. With this method, the changes have to be made at the code level, and the power is taken away from the site administrator. Whether this is good or bad is a choice you have to make based on the convenience and needs of your site. The predictability of the new menu management system is another problem. With the menu system, your site navigation is arbitrary. It’s not directly tied to your content. You can automatically add top-level pages to a menu, but you cannot automatically add the child pages. In addition, the page hierarchy isn’t refl ected in the menu system. You have to manually manage the menu above and beyond the management of content. One possible trap is that when you delete a page from your site, the menu item remains. This disconnect can be both a pro and a con. On the one hand, the site administrator really has complete control over the menu content. He can hand-craft the menu to meet his needs. However, it is important for your site administrator to understand that he has to manage the content and the menu. There can be confusion when some aspects of the site navigation are programmatically created in the theme templates, but the menu is handmade.

    c09.indd 247 12/6/12 1:21 AM 248 ❘ CHAPTER 9 THEME DEVELOPMENT

    For example, imagine you have built a new theme for a client that has multiple product pages. These are special pages to highlight individual products with their specifi c information and details. To further enhance the user experience, and perhaps cross-sell some product, you have built a custom page template for the products pages that has a built-in related products section at the bottom. This related products section is generated by code in the imaginary t_product_page.php custom page template. The challenge is that when a site administrator adds a new product page, it will show up automatically in this related product page code on some other sites, but will not show up automatically in the menu. The site administrator will have to manually edit the menu to add this page. This is not a devastating issue; it’s just disconnected and an extra step. The challenge is in empowering the site administrator and asking him or her to manually confi gure things, versus simply adding code to have things happen automatically.

    Widget Areas Widget areas work very much the same way as menus. In your functions.php fi le, you identify and name different areas for different parts of the site. These are often thought of as sidebars but can be so much more. Do not restrict yourself to thinking widget areas only belong in the sidebars of your site. Many themes, including Twenty Eleven, are expanding the number and location of widget areas to include the nameplate or header area, the footer, and even the middle of the loop. You have seen themes that have hundreds of widget areas. The nice thing about widget areas is their fl exibility. The number of widgets that can be placed into a widget area is immense. Having multiple widget areas empowers the site administrator to control content in areas of the site that are really outside the primary content areas of the site. As previously mentioned, setting up widget areas is very similar to setting up menus. In your functions.php fi le, you must identify and name your locations. Because widget areas have more fl exible and varied content than menus, which are pretty much unordered lists, you generally pass some HTML wrapper information for use when rendering the widgets. Also, because widget areas have evolved from the traditional sidebar use, the function to register them is still named register_sidebar(), but again, don’t let this pigeonhole them. Here is a widget area code snippet from Twenty Eleven:

    register_sidebar( array( 'name' => __( 'Main Sidebar', 'twentyeleven' ), 'id' => 'sidebar-1', 'before_widget' => '

    ", 'before_title' => '

    ', 'after_title' => '

    ', ) );

    Similar to menus, you pass the function a friendly name for use in the Control Panels and an identifi er for use in the theme templates. Beyond that, the additional parameters are for styling the widget consistently. In this example, Twenty Eleven is using HTML5 aside elements to wrap each widget and widget titles in h3 tags. This code notifi es WordPress that there is a widget area available for content.

    c09.indd 248 12/6/12 1:21 AM Other Theme Enhancements ❘ 249

    The next step is to assign the HTML real estate position to this widget area. For this example, you will look at sidebar.php in Twenty Eleven, but again, widget areas can be anywhere in your template. Here’s the code:

    A couple of things are going on in this code snippet. First and foremost, the fi rst line is looking to see if the widget area named sidebar-1 has content assigned in the Control Panel, and if it does, it will display it. This is one of the big benefi ts of widget areas: if they do not have content, they do not display unless you follow the paradigm outlined in the preceding code. In this case, if the widget area does not have widgets assigned to it, then the default content will display. In the preceding example, if no dynamic content was set up in the Control Panel, this widget area will show the archives and the meta widgets automatically. How you set this up depends on your needs. You can just as easily provide widget areas with no default content to expand the possibilities of your theme.

    Post Formats This is simply a feature fl ag that you can turn on or off in your functions.php fi le. If your theme intends to use the built-in post formats, or a subset of them, you can enable them. Post formats are essentially a way to customize the display of certain types of posts. This is most commonly seen when you are using WordPress for blogging or archiving a journal. The different post formats can be used to pull different loop HTML rendering as you saw earlier in this chapter. This allows you to present quotes or links differently than full posts. WordPress currently supports ten post formats for varying content. You can enable any or all of these formats depending on the goals of your theme. The standard format is for traditional posts and is enabled automatically. This is the format used in WordPress forever; it just now has a name assigned to it.

    c09.indd 249 12/6/12 1:21 AM 250 ❘ CHAPTER 9 THEME DEVELOPMENT

    In addition, there are nine new formats as of WordPress 3.1. Here they are, with the recommended styling, although using the fl exibility of WordPress, you can style these to fi t your needs: ➤ Aside — This is similar to a quick note. It is usually presented without the post title. ➤ Audio — Obviously, this format is for an audio fi le, perhaps a podcast or a band releasing a single. ➤ Chat — This format is usually a chat transcript. This is usually styled using the pre HTML element to keep the line breaks. ➤ Gallery — This is a gallery of image media attachments. The actual post content will typically contain a gallery shortcode. This format is for styling the gallery. ➤ Image — This post format is for a single image. The single image can be embedded in the post content, or a URL in the post content will pull that image to your site. ➤ Link — This format is for a link to another URL. Generally, these are presented as simply the link, without the title. This format is often used for creating your own bookmark site or reminders about URLs that interested you. ➤ Quote — Another self-explanatory one — this format is for quotes. This format is used to archive quotes that have some special meaning to you. You can present this format without the title or fl ip it around and use the title information as the attribution to the quote. ➤ Status — Think of this format as Twitter or Facebook updates. Typically presented without a title. You can use this format to make your own Twitter clone. ➤ Video — Similar to image and audio, this format is for presenting a single video to your visitors. The video can be embedded in the content or as an external URL. To enable post formats for your theme, simply select which formats you intend to use and pass them as a parameter array. For example, the Twenty Eleven theme is supporting a subset of the previous formats:

    add_theme_support( 'post-formats', array( 'aside', 'link', 'gallery', 'status', 'quote', 'image' ) );

    Remember that the standard post format for traditional post content is always enabled. Post formats can tailor your theme to specifi c niche uses or broaden your theme to present different content types in unique and customized ways.

    Theme Settings Many of the theme frameworks offer a special Theme Settings Control Panel for customizing the theme framework. This is a coded solution that comes from the theme developer but creates a new Control Panel with whatever settings the theme developer has opted to include. This is yet another feature that is a balance of empowering the site administrator as opposed to handling aspects in the theme template code. Creating your own Theme Settings Control Panel is a pretty complex endeavor that is outside the scope of this book. Each theme is different, and it’s the developer’s goals that determine which features or aspects of your theme are confi gurable.

    c09.indd 250 12/6/12 1:21 AM Theme Hierarchy and Child Themes ❘ 251

    To create a Theme Settings Control Panel, you fi rst have to build the control panel code and forms and register them with WordPress. You then have to take the confi gurable options and use them in your template fi les. The control you give to your site administrator can be very powerful, but it does make developing your templates a little trickier. For more information about creating your own Theme Settings Control Panel, check out the WordPress Codex and other theme frameworks; there are also many good tutorials online.

    Theme Customizer Some of the functionality of Theme Control Panel has been moved into the new Theme Customizer, which was introduced in WordPress 3.4. This is a new Control Panel for customizing the theme. The really neat thing is that it can show the site administrator a live preview of the site while he is customizing it. This is truly the biggest benefi t of this component. This real-time live preview allows the site administrator to experiment with the site and see how it would look without affecting the live production site until he is done. In the past, this process involved making a change in a Theme Settings Control Panel, discussed previously, in the code, or in other customizable places, and publishing those changes to the live site. Then the site administrator had to browse to the live site to see the outcome. At the same time, any visitors browsing the site saw the in-process design changes. The change may or may not have been what the site administrator intended, or worse, may have broken some display aspect. The live preview presents this whole process while allowing great control. The Twenty Eleven theme is the fi rst theme that is using this new set of functions to any great extent, although it is expected that many more themes will be using it by the time this book is published. Basically, the Theme Customizer uses the WordPress settings API to store confi gurable design information. Then using specially crafted functions in the functions.php fi le, WordPress applies the changes to the HTML.

    In the Twenty Eleven function for changing the header color, the functions.php fi le injects a new CSS stanza into the HTML head that overrides the style sheet. In practice, this is not really optimal code for a high-traffi c site; however, this is yet another example of trading optimization for site administrator confi gurability.

    THEME HIERARCHY AND CHILD THEMES

    So far in this chapter, you have looked through the template fi les that make up a complete theme, focusing on the stock Twenty Eleven theme. You even considered renaming it and making your custom theme in that new directory using the Twenty Eleven theme as a starter theme. This is a good way to get started with theme creation as it helps you dive into the internals of how a theme works. And, in the real world, this is how many development teams work today. This method works well because you know exactly where your template fi les and CSS fi les are that need to be edited. The whole theme is self-contained, which minimizes workfl ow and deployment efforts — not that it’s perfect, but it is very solid. However, with the release of WordPress 2.7, child themes became a functional reality. While you could implement child themes prior to WordPress 2.7, it was not until template fi le inheritance

    c09.indd 251 12/6/12 1:21 AM 252 ❘ CHAPTER 9 THEME DEVELOPMENT

    was included that child themes became a viable development option. Child themes let you take an existing theme or theme framework and use the best parts of it, and then extend and modify it, license permitting, to meet your own theme’s needs while maintaining future updates to the parent theme. After you have the basics of theme development down, it is highly recommend that you pick a theme framework you are comfortable with (a few are mentioned in the next section) and create child themes. Child themes are the future of theme development for WordPress. This concept is pretty revolutionary for several reasons. First, it certainly opens the door for theme frameworks. Starting with a solid foundation, you can now make countless variations on the theme simply through inheritance. Theme frameworks tend to be very plain, and intentionally so, but by using child themes, you can inherit all the CSS semantic hooks and microformat gooey centers and build your own candy shell around it, basically taking the best parts and making a new creation. Second, updates to the parent theme or theme framework will not overwrite your customizations. Previously, when you made modifi cations to your copy of the theme, you had to keep track of the changes you made so that you could reapply them when the theme was upgraded. This can be somewhat automated via a source code management solution, but it is arduous at best, when it works. And there is always the day when you forget to make a modifi cation to the updated fi les. Third, child themes led the way for auto-updating themes in the Theme Manager. Occasionally theme templates are vulnerable to security exploits such as cross-site scripting. Using a properly inherited child theme means the parent theme can auto-update to address security issues while not affecting your child theme. This creates a more secure implementation for your site. There are a couple of caveats here. The functionality that keeps your child theme customizations unaffected works both ways. If you override a particular template fi le with your own customizations, any enhancements to the parent template fi le of the same name will not cascade to your unique fi le. In practice, this could create a false sense of security, where you may have copied a poorly coded template fi le to modify, and then changes were made to the parent version but your fi le is unaffected and still vulnerable. That is, because you are carrying forward the vulnerability in your extended code, you continue to override any repaired code. This would not only apply to security amendments but would also apply to any feature enhancements. That is, child themes do not totally remove you from the code management process. In addition, there is a little bit of CSS overhead here. Generally, a child theme builds upon the CSS of the parent theme. And, in truth, that is exactly how CSS is designed to work, hence the word cascading in the name. So, for this to work in child themes, the child theme has to include the CSS from the parent theme, even the rules that get overridden in the child theme. That means that the byte weight of the CSS in your child theme may be quite a bit larger than what you actually use in the browser, but you have to transfer it all anyway. That said, child themes are a fantastic feature of WordPress, and we recommend using this methodology when the situation warrants it. Certainly maintaining a pristine theme framework and then extending that theme to individual sites adds to the benefi ts of a common theme vernacular of CSS and functions, as well as the other benefi ts mentioned previously. Again, using child themes is the future for WordPress theme development and is the best practices recommendation. Take a look at how child themes actually operate and what is required in making your fi rst child theme. The fi rst thing you need to do is fi nd the theme you are using as the parent. Your parent

    c09.indd 252 12/6/12 1:21 AM Theme Hierarchy and Child Themes ❘ 253

    theme does not have to be labeled a theme framework. You can extend any theme as long as it meets the following conditions: ➤ The licensing permits you to extend or modify the theme. ➤ The parent theme is not a child theme itself. In this example, you will continue to build on the stock Twenty Eleven theme, but you could use any theme or theme framework as the parent. As alluded to earlier in the chapter, to make your custom theme a child theme of another theme, you must add a line to the header information of your style.css fi le. This line informs WordPress of the location of the parent theme. Therefore, the variable in the comment should be the folder name of the theme. Although it depends on the server, is best to be case-sensitive when naming your theme. In this instance, you are adding the following line:

    Template: twentyeleven

    To illustrate this, the entire header comment block from the sample child theme reads as follows:

    /* Theme Name: A Twenty Eleven Child Theme Theme URI: mirmillo.com Description: A sample child theme Author: David Damstra Author URI: mirmillo.com Template: twentyeleven Version: 1.0 */

    As discussed previously, having the style.css fi le with the properly formatted header information in your uniquely named folder registers your theme with WordPress. The next step is to import the CSS from the parent theme so that your custom theme has base rules to work with:

    /* import the base styles */ @import url('../twentyeleven/style.css');

    At this point, you can activate your theme in the WordPress appearance Control Panel. You have a fully functional child theme of the Twenty Eleven theme. It will look exactly like the Twenty Eleven theme because it is, in essence, a practical copy of the parent theme. The remainder of your style sheet operates like traditional CSS where you can override previous rules through the CSS rules of specifi city and precedence, including the order in which they are listed — because your custom styles appear later, they will take precedence. Again, working with CSS is outside the scope of this book, so let’s, go ahead and extend the child theme a little bit. Although you can make these same changes with the Theme Customizer presented earlier, for the sake of this example make the changes in CSS. Update the child theme with a nice pink background and change the base font. Here’s what the complete style.css fi le might look like:

    /* Theme Name: A Twenty Eleven Child Theme Theme URI: mirmillo.com

    c09.indd 253 12/6/12 1:21 AM 254 ❘ CHAPTER 9 THEME DEVELOPMENT

    Description: A sample child theme Author: David Damstra Author URI: mirmillo.com Template: twentyeleven Version: 1.0 */

    /* import the base styles */ @import url('../twentyeleven/style.css');

    body { background: #E0A3BD; color: #333; font: 100%/1.5 calibri, arial, verdana, sans-serif; }

    From here on out your browser developer tool is your best friend. Use the inspector to see the current style rules applied to various elements and make the appropriate changes in your child theme’s CSS fi le. Again, remember to follow the precedence and specifi city rules of CSS. Your child theme can be as simple or complex as you make it. You can create a completely unique theme by simply editing the style sheet, as you have done previously. Or your child theme can turn into a completely new theme with all new templates, although this most likely defeats the purpose of using a child at all. Here is how it works. When WordPress makes a decision on which template fi le to use, fi rst it scans your child theme directory for that fi le. If that fi le does not exist, the parent theme directory is scanned. WordPress will prefer your template fi les over those of the parent theme, which means you can override the functionality of specifi c templates while maintaining the core of the parent theme. Or, your child theme could introduce custom page templates, but the foundation templates are pulled from the parent. There is a wide scope of opportunities here, although keep in mind the previously mentioned limitations. The easiest way to accomplish this is to copy the template fi le you want to modify from the parent theme directory into your child theme directory and then modify as needed. For example, the author template in the Twenty Eleven theme is perfectly functional, but suppose you want to change the size of the author image in this template. Again, this is an intentionally simple example and there are many ways to copy this.

    First, copy the author.php template from the Twenty Eleven theme into your child theme directory. Second, edit your child copy to change the avatar image size around line 46 of the code. It might read something like this:

    In this example, you doubled the size of the image from 60 pixels wide to 120 pixels wide. You can see an example of what this looks like in Figure 9-5.

    c09.indd 254 12/6/12 1:21 AM Theme Hierarchy and Child Themes ❘ 255

    FIGURE 9-5: Child themes make it easy to apply styles to specifi c pages.

    You can further extend the child theme with your own functions.php fi le. WordPress automatically includes the parent theme’s functions, but in addition, it also includes your child theme functions. You do have to be conscientious about function naming. Be very careful not to create functions in your own theme that have the same name as a parent theme function. If you need to override functionality, the authors’ advice is to make a new function in your own theme with a new name to avoid name resolution confl icts and adjust the template fi les as necessary to call your function instead. In addition, theme frameworks have advanced signifi cantly and many include multitudes of custom hook locations and fi lters for you to capitalize on. Using these custom hooks and fi lters, you can actually use your child theme’s functions.php to inject and modify the parent theme’s HTML without making a second copy of the template fi les. This is a process that builds on the topics covered in Chapter 8. This is a much more complex way to build child themes but provides you the most safety because you are able to update the parent theme in the future as your child theme uses the hooks of the parent to amend and change the theme to make it your own. As you can see, child themes are yet another powerful tool in the WordPress theme arsenal. You can quickly get a theme up and running using an established theme as a base, and then modify and extend only what is required to create your own theme, all the while future proofi ng the upgradeability of your foundation theme. This really is a game changing feature once you grasp it. It’s not a simple concept, especially when you use a framework with custom hooks and fi lters. Even if your design team hand-codes

    c09.indd 255 12/6/12 1:21 AM 256 ❘ CHAPTER 9 THEME DEVELOPMENT

    each theme from scratch, having a foundation to start from offers a number of benefi ts, including increasing effi ciencies because commonly implemented features are already being implemented consistently, and they also have the familiar CSS and markup vocabulary that your team is intimately familiar with. Regardless of whether your parent theme is one of the popular theme frameworks or something you have developed in-house, the benefi ts are quite tangible.

    PREMIUM THEMES AND OTHER THEME FRAMEWORKS

    Thus far in this chapter, you have explored the Twenty Eleven theme and used it in most of the examples. But certainly, it is not the only theme or theme framework out there and may not be the best match for you or your development team. Many of these other themes you will look at briefl y include another layer of abstraction in them or a fl urry of functions in thefunctions.php fi le. Generally, this abstraction brings the ability to modify the theme into the WordPress Control Panel. This may be ideal for certain clients who are not PHP- savvy and want that control delegated to the site administrator rather than the developers. The best way to make a choice here is to try each theme out and kick the tires. Find a theme or theme framework that fi ts your coding style and needs and then run with it. Remember that you can make child themes or modify themes to meet your needs (license permitting, of course) from basically any theme out there, but with the new child theme functionality in WordPress 2.7, there has been a growth spurt in theme frameworks. You take a cursory look at some of the more popular theme frameworks (at the time of this writing) here. Many are out there, so be sure to look around. Please keep in mind that various terms are thrown about with regard to themes. Magazine themes and premium themes mean different things to different people. Sometimes premium means the theme costs money; other times it means it includes an administration control panel. Some themes that are available for a fee are called commercial themes. A theme framework is typically developed to be built upon. Although they may stand by themselves, they are intentionally written for extension. Starter themes are meant to be forked and edited in place. Your needs may vary with every project, and certainly you need to fi nd a theme that speaks to you. The following is just a sampling of some more popular themes that we have used in the past. There are many more to choose from and we are not endorsing any one over another.

    Bones Theme Bones is a starter theme by Eddie Machado. The Bones theme is built on top of HTML5 boilerplate as a foundation, meaning that it is pretty forward thinking. We like Eddie’s philosophy that child themes and theme frameworks are great, but sometimes they make things more complicated than they need to be. The simplicity of taking a starter theme and making a one-off project theme for a site appeals to many seasoned developers and this is the goal of the Bones theme. In addition to HTML5, the Bones theme is a responsive base using media queries and a mobile-fi rst mentality. Responsive web design is covered a little more in Chapter 12, but know that if you are making sites that you expect to be viewed on smartphones, having responsive views from the get-go helps. Bones also includes LESS and Sass CSS functionality for advanced developers. Check out the Bones theme at http://themble.com/bones.

    c09.indd 256 12/6/12 1:21 AM Premium Themes and Other Theme Frameworks ❘ 257

    Carrington Theme Carrington is a theme framework by Alex King’s Crowd Favorite. Carrington is designed for child themes and is built to be very modular, meaning you add in what functionality you need. Carrington does this by abstracting code into small components. This allows code reusability and keeps things organized. At fi rst glance, Carrington can seem complicated, but once you digest the processes involved, it makes sense. Crowd Favorite also has some other interesting WordPress projects, including RAMP, which was mentioned in Chapter 3, and the Carrington Build theme, which is a drag-and-drop pay layout generator. Learn more about the Carrington theme at http://carringtontheme.com.

    Genesis Theme The Genesis theme framework by Brian Gardner’s StudioPress is one of the most, if not the most, popular theme frameworks around. The Genesis theme framework is designed exclusively for child themes, and StudioPress even sells many variations. To this end, the Genesis framework has many additional hooks and fi lters for customizing your child theme to make it unique. The Genesis theme framework is an evolution of the Revolution theme. The Revolution theme was a pioneer in WordPress themes and really raised the bar to change theme development standards. Revolution was one of the fi rst themes to embrace the magazine theme style that helped WordPress transcend the blog stereotype and become a viable CMS solution. Magazine-style themes made WordPress look less bloggy and more like a traditional website. In addition, the Revolution theme was one of the early commercial themes. The Revolution theme has since been retired and is no longer available. However StudioPress has taken its experience and created Genesis and many child themes.

    You can fi nd the Genesis Theme online at http://studiopress.com.

    Hybrid Core Theme The Hybrid theme by Justin Tadlock is free, but charges a club membership fee for access to the theme documentation, tutorials, and support forums. This theme includes a nice control panel to toggle various features and CSS hooks on and off. The Hybrid theme has several ready-made child themes available. This theme has rich CSS hooks throughout the posts and body tags. It also includes numerous widget- ready areas and many custom page templates in the stock installation. These custom page templates cover a variety of use cases and really add to the theme, if you know how to use them. The Hybrid Core theme is modular with many features and extensions that can be enabled if you need them.

    You can fi nd more information about the Hybrid Core theme at http://themehybrid.com/.

    Roots The Roots theme, developed by Ben Word, is another starter theme. Like Bones, this theme is based on HTML5 boilerplate and also Twitter Bootstrap. Because this theme is based on Twitter Bootstrap, it is also a responsive theme for mobile devices.

    c09.indd 257 12/6/12 1:21 AM 258 ❘ CHAPTER 9 THEME DEVELOPMENT

    One of the interesting features of the Roots theme is the included .htaccess fi le for use on Apache web servers. The .htaccess cleans up URLs throughout your site by redirecting some of the WordPress recognizable paths for images, CSS, JavaScript, and plugins to be more traditional URL paths. In addition, the .htaccess rewrites the search URLs to be more human-readable and makes most asset URLs root relative. All in all, this theme does a good job of further cleaning up WordPress URLs and making them more semantic.

    The Roots theme is available online at http://www.rootstheme.com/.

    StartBox Theme StartBox is a theme framework developed by Brian Richards for building child themes. Like the Hybrid theme, the framework itself is free, but there is a club membership fee for access to the child themes, tutorials, and support. The StartBox theme includes an extendable Theme Options Control Panel and many built-in shortcodes for commonly requested tasks such as call-to-action buttons and social media links. An interesting feature of the StartBox theme is the sidebar manager, which allows the site administrator to create widget areas through the Control Panel. The StartBox theme, like many of these other themes, has the venerable Sandbox theme as an ancestor, meaning it has many CSS hooks built in throughout the HTML.

    Learn more about the StartBox theme online at http://wpstartbox.com/.

    Thematic Theme Thematic is a theme framework developed by Ian Stewart. The Thematic theme is free with a decent breadth of existing child themes available. Child themes are available both free and commercial. Thematic also includes a minimal administration control panel to modify limited information. Two features really stand out. First, this theme’s ancestry includes the Sandbox theme. The rich semantic CSS hooks that are found in Sandbox have been brought into Thematic and extended. Second, this theme includes 13 widget-ready areas. Thematic includes many search engine optimization and layout features. You can fi nd more information about the Thematic theme at http://themeshaper.com/thematic/.

    SUMMARY

    In this chapter, you covered how to use themes to organize, structure, and present your content. Your theme is the face of your site, and no matter how good your content is, this presentation is what really seals the deal on the user experience. A theme that looks amateurish can hurt the credibility of your site, whereas a sharp, professional theme can enhance the whole experience. In the next chapter, you will look at taking external content sources and incorporating them into your WordPress site to further develop the quality of your content and the user experience.

    c09.indd 258 12/6/12 1:21 AM 10 Multisite

    WHAT’S IN THIS CHAPTER?

    ➤ Understanding WordPress Multisite ➤ Diff erences between Multisite and standard WordPress ➤ Installing and confi guring a Multisite network ➤ Coding for Multisite ➤ Multisite database schema

    WROX.COM CODE DOWNLOADS FOR THIS CHAPTER The wrox.com code downloads for this chapter are found at www.wrox.com/remtitle .cgi?isbn=9781118442272 on the Download Code tab. The code is in the Chapter 10 down- load fi le and individually named according to the code fi le names throughout the chapter. WordPress Multisite is a powerful core feature in WordPress. When enabled, Multisite allows you to create multiple websites with a single install of WordPress. This makes it easy to rapidly launch new WordPress websites. A Multisite network can even allow open user and site regis- tration, enabling anyone to create a new site in your network. The largest WordPress Multisite network is WordPress.com, which is a great example of what Multisite can be.

    WHAT IS MULTISITE?

    Prior to WordPress 3.0, Multisite was called WordPress MU (or multi-user) and was a separate software package that needed to be downloaded and installed. WordPress 3.0 merged MU into the core of WordPress, and WordPress Multisite was born. Multisite is not enabled by default, so it’s important to understand the differences before enabling Multisite in your WordPress installation.

    c10.indd 259 12/6/12 1:32 AM 260 ❘ CHAPTER 10 MULTISITE

    Multisite Terminology It’s important to understand the terminology used throughout this chapter when working with WordPress Multisite. Two important terms in Multisite are network and site. A network is the entire Multisite installation, or the network. A site is a single site inside the network. Therefore, WordPress Multisite is a network of sites. Another important term is Blog ID. The Blog ID is a unique ID assigned to every new site created in Multisite. Many of the functions and code examples will reference the Blog ID. Sometimes this is also referred to as the site ID. Remember that WordPress was originally built as a blogging platform but has evolved over the years into a full-fl edged content management system. Therefore many of the functions and code in WordPress still reference items as “blogs” when really they are sites.

    NOTE Don’t let the term “blog” confuse you. A blog in WordPress Multisite is actually a site in the network. A Blog ID, as referred to by many functions and code examples, is the unique ID of the site in the network.

    Diff erences When you install standard WordPress, you are installing a single website to run on WordPress. WordPress Multisite enables you to run an unlimited number of websites with a single install of WordPress. When enabling Multisite, you need to determine how sites will be viewed in WordPress, either using subdomains or subdirectories. The following is an example of both: Subdirectory Example ➤ http://example.com/site1 ➤ http://example.com/site2

    Subdomain Example ➤ http://site1.example.com/ ➤ http://site2.example.com/

    As you can see, this is a pretty big decision and one that should be carefully considered. With a plugin, there are ways to map top-level domains to any site in your network (i.e., http://mywebsite.com), which is covered later on in this chapter. Themes and plugins are also treated differently in Multisite. Individual site administrators can enable themes and plugins on their site, but they can’t install them. WordPress Multisite also introduces a new user role: Super Admin. Super Admin users have access to the Network Admin section of Multisite. This section is where all Multisite confi guration occurs. Super Admins also have full access to every site in the Multisite network, whereas normal Administrators only have access to the site they are an administrator of.

    c10.indd 260 12/6/12 1:32 AM What Is Multisite? ❘ 261

    Advantages of Multisite WordPress Multisite has a number of advantages over standard WordPress. The biggest advantage of Multisite is that you have a single install of WordPress to administer. This makes updates for WordPress, themes, and plugins much easier. If you have a WordPress Multisite network of 50 sites, and a plugin update is released, you need to update that plugin only once and it will affect all sites in your network. If each of the 50 sites were a separate install of WordPress, you would have to update that plugin 50 separate times. Aggregating content is also another big advantage. It is very easy to share content between your sites in a Multisite network. For example, if you have 50 sites in your network, you could easily aggregate posts from every site to your main blog to showcase your network of sites and content. The biggest advantage to using Multisite is the speed in which you can launch new sites. With just a few clicks you can create new sites in your network. These sites can share themes, plugins, and even users.

    Enabling Multisite Enabling the Multisite feature of WordPress is a pretty straightforward process. The fi rst step is to add the following line of code to your wp-config.php fi le:

    define( 'WP_ALLOW_MULTISITE', true );

    This line of code should be added just above the comment that reads:

    /* That's all, stop editing! Happy blogging. */.

    Save your wp-config.php fi le and upload to your server. Now log in to the admin dashboard of WordPress and you’ll notice a new submenu item for Tools ➪ Network Setup, as shown in Figure 10-1. The Network Setup screen will vary depending on your current WordPress setup. If your setup allows it, you will choose either Subdomains or Subdirectories for your Multisite setup. You also need to verify that the Server Address, Network Title, and Admin E-mail Address values are correct. These are fi lled in automati- cally by WordPress, but you can modify the values if needed. After you have confi rmed that the settings are what you want, click the Install button to install Multisite in WordPress. The fi nal step to enabling Multisite will be presented on the screen; it’s a series FIGURE 10-1: Network Setup of manual changes you need to make to WordPress. The fi rst step is to cre- submenu ate a blogs.dir directory in your /wp-content directory. This new directory will store all media uploaded throughout your Multisite network. All media in Multisite is uploaded to wp-content/blogs.dir/BLOG_ID/files/YEAR/MONTH. Permalinks for media fi les look like this:http://example.com/files/2013/10/Halloween.png .

    c10.indd 261 12/6/12 1:32 AM 262 ❘ CHAPTER 10 MULTISITE

    The next step is to add some code to your wp-config.php fi le. This code defi nes the base set- tings for Multisite, and will vary depending on your setup. The following is a code example for a Subdirectory install of Multisite under the example.com domain.

    define( 'MULTISITE', true ); define( 'SUBDOMAIN_INSTALL', false ); $base = '/'; define( 'DOMAIN_CURRENT_SITE', 'example.com' ); define( 'PATH_CURRENT_SITE', '/' ); define( 'SITE_ID_CURRENT_SITE', 1 ); define( 'BLOG_ID_CURRENT_SITE', 1 );

    The fi nal step is to replace your .htaccess fi le rules with the new rules provided:

    RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L]

    # uploaded files RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L]

    # add a trailing slash to /wp-admin RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

    RewriteCond %{REQUEST_FILENAME} -f [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^ - [L] RewriteRule ^[_0-9a-zA-Z-]+/(wp-(content|admin|includes).*) $1 [L] RewriteRule ^[_0-9a-zA-Z-]+/(.*\.php)$ $1 [L] RewriteRule . index.php [L]

    These rules may differ depending on your Multisite setup, so make sure you copy the code provided in the Network Setup screen when enabling Multisite in WordPress. After making the required changes, you will be required to log back in to WordPress. The easiest way to tell when Multisite is enabled is through the WordPress admin bar. You’ll notice a new menu item named My Sites. When hovering over this menu, the fi rst Submenu link is Network Admin, which is the main admin dashboard for Multisite. The My Sites menu will also list all sites you are a member of in your network, as shown in Figure 10-2. WordPress Multisite is now enabled and ready to use! FIGURE 10-2: Multisite Network menu WORKING IN A NETWORK

    Now that you know how to enable Multisite in WordPress, it’s important to understand how to manage your network. This section covers the Multisite Network Admin section of WordPress and how to manage a network.

    c10.indd 262 12/6/12 1:32 AM Working in a Network ❘ 263

    Network Admin The WordPress Multisite Network Admin is the central hub for all Multisite management of your network. You can access the Network Admin under the My Sites menu in the WordPress admin bar or by visiting http://example.com/wp-admin/network/. The Network Admin should look very familiar because its layout and style are very similar to the standard WordPress admin dashboard.

    Creating and Managing Sites To view a list of all sites in your Multisite Network, visit the Sites menu. Here you will see every site registered in Multisite, regardless of the status of the site. The list screen gives you some important information, such as the site name, last updated date, registered date, and all users that are members of that site. To edit any site’s settings, click on the site name to bring up the Edit Site screen. The Edit Site section allows you to edit all set- tings of a specifi c site in your network. You’ll notice tabs for each settings section, as shown in FIGURE 10-3: Edit Site section Figure 10-3. The Info tab allows you to edit the domain and path of the site. These two settings are not confi gu- rable for the main site in your network. You can also change the Registered and Last updated date and timestamps. The fi nal editable item is the Attributes, or site status, setting. Every site in your network can have one of the following fi ve site statuses: 1. Public — Site is public if privacy is set to enable search engines. 2. Archived — Site has been archived and is not available to the public. 3. Spam — Site is considered spam and is not available to the public. 4. Deleted — Site is fl agged for deletion and is not available to the public. 5. Mature — Site is fl agged as mature.

    The only two statuses that don’t remove the site from public viewing are Public and Mature. The Users tab allows you to administer what users have access to this site. This section will list all users of the site with their role. You can also add new users to the site. The next section cover users in Multisite in more detail. The Themes tab allows you to enable or disable themes for the site. Enabling a theme on this screen does not actually activate the theme for the site you are editing, but rather, makes that theme an available option for the site administrator to enable should he choose to do so.

    c10.indd 263 12/6/12 1:32 AM 264 ❘ CHAPTER 10 MULTISITE

    The Settings tab enables you to edit all other settings for the site. There are a lot of options on this screen. As a good rule, if you don’t know what you are changing, you probably shouldn’t change it.

    Working with Users and Roles Users in a Multisite network work differently than in standard WordPress. The major difference is that each site in the network can have a different set of users. Users can also be a member of mul- tiple sites in your network and even have a different user role for each site. For example, you could be an Administrator on site A, but only an Author on site X. If Allow New Registrations is enabled under the Network Settings menu, visitors can register new user accounts in WordPress. A New User is not automatically a member of every site in your network, but rather the main (fi rst) site in your network. For example, if your network features two sites, a Halloween site and a Christmas site, any visitor who registers would be a member of the Halloween site but not the Christmas site. Of course, you could always add this user to the Christmas site later. To view all users in your Multisite network, visit the Users menu. Here you will see a list of all users in the network along with their names, e-mail addresses, registered dates, and a list of sites they are members of. If the user is a Super Admin, you will see that information listed next to her username. You can easily add, edit, or delete users from your network in this section.

    Themes and Plugins Multisite handles themes and plugins differently than standard WordPress. All sites in your net- work can run the same plugins and themes, or they can run a completely different set of plugins and themes. The fl exibility of this really showcases the power of Multisite in WordPress.

    Themes To view all themes installed in WordPress, visit the Themes menu. The Network Admin Themes section lists all themes in a list similar to the standard WordPress Plugins section. The major dif- ference is that rather than an Activate link for each theme, you’ll notice a Network Enable link instead. Network Enabling any theme listed will make that theme an available option for all sites in your network. This doesn’t actually activate the theme, but rather makes the theme available to site administrators under the Appearance ➪ Themes menu in WordPress. This allows you to control what themes are available for your site administrators to choose from.

    Plugins Plugins work differently from themes in Multisite. Plugins can be Network Activated, which means the plugin will run on every site in your network. If a plugin is not Network Activated, it can still be activated at the site level. This means that you can run plugins on any, or all, sites in your network. To view all plugins available for use, visit the Plugins menu. Here you’ll see a list of plugins that have been downloaded to WordPress. Clicking the Network Activate link will activate the plugin across every site in your network. If a plugin is not Network Activated, it will be available to activate at the site level under the standard Plugins menu.

    c10.indd 264 12/6/12 1:32 AM Coding for Multisite ❘ 265

    NOTE It’s important to verify that a plugin is Multisite-compatible prior to net- work-enabling the plugin. If the plugin hasn’t been tested on Multisite, or coded properly, there is a chance it could break your network when activated network wide.

    Settings The Settings menu lets you set network-wide settings in Multisite. Here you can enable user account registration and even site creation for your users. Enabling both of these features would allow visi- tors to register user accounts and even create new sites in your network. You can also set what fi le types are allowed for upload, total site upload space, and the max upload fi le size. If you plan on launching a very large Multisite network, limiting site upload space and max upload size could save you a massive amount of disk space. By default, the Plugins menu is hidden from site administrators. This allows only Super Admins to activate and deactivate plugins at the site level. If you want to enable the Plugins menu for regular administrators, you can do so under the Menu Settings section.

    Domain Mapping One very common feature for Multisite users is domain mapping. Earlier, you considered the two default site confi gurations for Multisite: Subdirectory or Subdomain. But what if you want each site in your network to have a unique domain name? There’s a plugin for that! The WordPress MU Domain Mapping plugin (http://wordpress.org/extend/plugins/wordpress-mu-domain- mapping/) allows you to do just that. This plugin makes it very easy to attach a top-level domain to any site in your network. The plugin also works with both Subdirectory and Subdomain setups. For example, instead of two sites like http://example.com/brad and http://example.com/myers, you could have http://brad.com and http://myers.com. All URLs will be served up using the top-level domain you have assigned to your sites.

    CODING FOR MULTISITE

    When Multisite is enabled in WordPress, you can take advantage of an entirely new set of Multisite- specifi c functions and APIs in your themes and plugins. Understanding these new features can help you make your themes and plugins Multisite-compatible.

    Blog ID Every site in your Multisite network has a unique ID associated with it. This unique ID is called the Blog ID. Almost every function you work with when writing Multisite-specifi c code will use this Blog ID. This is how WordPress determines what site you want to work with. The Blog ID is also used in the database table prefi x with each new site you create in your network. When you create new sites in Multisite, WordPress creates additional database tables to store that site’s content and settings. For example, if you create a second site in your network, WordPress will

    c10.indd 265 12/6/12 1:32 AM 266 ❘ CHAPTER 10 MULTISITE

    create new tables prefi xed like wp_2_posts where wp_ is the table prefi x you defi ned when installing WordPress, and 2_ is the Blog ID of the new site.

    The Blog ID is stored in the global variable $blog_id, as shown here:

    In Multisite, the $blog_id will always be the ID of the current site you are viewing. In standard WordPress, the $blog_id global variable will always be 1.

    Common Functions Some common functions are available when working with Multisite. The most important function when working with Multisite is the is_multisite() function, as shown here:

    This function determines whether Multisite is enabled and, if so, returns true. Anytime you plan on using Multisite-specifi c functions in WordPress, it’s extremely important that you verify that Multisite is actually enabled before doing so. If Multisite is not enabled, and you call a Multisite function, you will receive an error message and the site will break.

    Another useful function for retrieving site-specifi c information is the get_blog_details() function.

    The function accepts two parameters: 1. $fields — Blog ID, a blog name, or an array of fi elds to query against 2. $getall — Whether to retrieve all details

    Using this function, you can retrieve general site information for any site specifi ed.

    Running the preceding code example displays the following results:

    stdClass Object ( [blog_id] => 1 [site_id] => 1 [domain] => example.com [path] => /

    c10.indd 266 12/6/12 1:32 AM Coding for Multisite ❘ 267

    [registered] => 2012-10-31 19:01:47 [last_updated] => 2012-10-31 19:01:49 [public] => 1 [archived] => 0 [mature] => 0 [spam] => 0 [deleted] => 0 [lang_id] => 0 [blogname] => Halloween Site [siteurl] => http://example.com [post_count] => 420 )

    Switching and Restoring Sites One of the main advantages to using WordPress Multisite is how easy it is to aggregate content, and other data, between different sites in your Multisite network. There are two primary functions you can use to retrieve data from other sites in your network. The fi rst of these is theswitch_to_blog() function. This function enables you to switch to any site in your network:

    The function accepts two parameters: 1. $blog_id — The ID of the site you want to switch to. 2. $validate — Whether to check if the site exists before proceeding. The default is false.

    The second function is restore_current_blog(). This function does exactly what it sounds like: it restores the previous site after a switch_to_blog() function is called. There are no parameters for this function; simply call it after you are done gathering content and data from the site. Consider the following example that uses these two functions. In this example, you’ll create a cus- tom shortcode to retrieve the latest fi ve posts from a site in your network:

    add_shortcode( 'show_network_posts', 'prowp_get_network_posts' );

    First, register a new shortcode called show_network_posts using the add_shortcode() function. The new shortcode will accept one parameter, which is the Blog ID you want to display the latest posts from. Next, you’ll create the function to return the latest posts from a site in your network.

    function prowp_get_network_posts( $atts ) { extract( shortcode_atts( array( 'blog_id' => '1' ), $atts ) );

    //verify Multisite is enabled if ( is_multisite() ) {

    //switch to blog ID passed in

    c10.indd 267 12/6/12 1:32 AM 268 ❘ CHAPTER 10 MULTISITE

    switch_to_blog( absint( $blog_id ) );

    //create a custom loop $recent_posts = new WP_Query(); $recent_posts->query( 'posts_per_page=5' );

    $site_posts = '';

    //star the custom loop while ( $recent_posts->have_posts() ) : $recent_posts->the_post();

    //store the recent posts in a variable $site_posts .= '

    ' .get_the_title().'

    ';

    endwhile;

    //restore the current site restore_current_blog();

    }

    //return the posts return $site_posts;

    }

    As always, you need to verify that Multisite is enabled using the is_multisite() function check. Next, use the switch_to_blog() function to switch to the Blog ID passed in through the shortcode. If the user does not set the Blog ID in the shortcode, it will default to Blog ID 1. Now that you’ve switched to the site, you’ll create a custom loop using WP_Query to pull the latest posts from the site. Next, loop through the WP_Query results, storing each post in a variable called $site_posts.

    After the loop has completed, you need to run restore_current_blog() to switch back to the pre- vious site you were viewing. If you do not run this function, WordPress stays on Blog ID 10, so any subsequent loops or custom code will assume you are still on Blog ID 10, when in fact you are not. The fi nal step is to return the variable $site_posts, which contain the latest fi ve posts from Blog ID 10. That’s it! Now you can easily display the most recent blog posts from any site in your network using the shortcode: [show_network_posts blog_id="10"]. Listing 10-1 shows the entire code packaged up in a plugin.

    LISTING 10-1: Multisite shortcode example (prowp2-multisite-shortcode.zip)

    c10.indd 268 12/6/12 1:32 AM Coding for Multisite ❘ 269

    Author URI: http://strangework.com License: GPLv2 */

    add_shortcode( 'show_network_posts', 'prowp_get_network_posts' );

    function prowp_get_network_posts( $atts ) { extract( shortcode_atts( array( 'blog_id' => '1' ), $atts ) );

    //verify Multisite is enabled if ( is_multisite() ) {

    //switch to blog ID passed in switch_to_blog( absint( $blog_id ) );

    //create a custom loop $recent_posts = new WP_Query(); $recent_posts->query( 'posts_per_page=5' );

    $site_posts = '';

    //star the custom loop while ( $recent_posts->have_posts() ) : $recent_posts->the_post();

    //store the recent posts in a variable $site_posts .= '

    ' .get_the_title().'

    ';

    endwhile;

    //restore the current site restore_current_blog();

    }

    //return the posts return $site_posts;

    }

    ?>

    The switch_to_blog() function is not just limited to site content, but can also retrieve other WordPress data including menus, widgets, sidebars, and more. Basically, any data stored in the con- tent database tables (wp_blogid_tablename) is available when using switch_to_blog(). Consider a different example. This time you’ll retrieve a specifi c menu from a site in your Multisite network.

    c10.indd 269 12/6/12 1:32 AM 270 ❘ CHAPTER 10 MULTISITE

    wp_nav_menu( 'Main Menu' );

    restore_current_blog(); ?>

    First run switch_to_blog() to switch to Blog ID 10. Next, use the wp_nav_menu() WordPress function to display a menu named Main Menu from the site. Finally, run restore_current_blog() to reset back to the current site you are viewing. The preceding code will display the Main Menu nav menu from Site ID 10 anywhere you run this code.

    It’s important to note that the switch_to_blog() function has the potential to generate very large SQL queries, which could cause performance issues with WordPress. It’s best to cache any data retrieved using this function in a transient, which enables data to be temporarily stored as a cached version. WordPress transients are covered in detail in Chapter 11.

    Another important note is that switch_to_blog() only changes the database context; it does not inherit the entire site confi guration.. This means that a site’s plugins are not included in a switch. If you switch to a site and try to execute a function specifi c to a plugin that is not enabled, you will receive an error message.

    Creating a New Site You’ve learned how to create new sites in the Network Admin of Multisite, so now you will see how to create new sites via code. To do so, you’ll use the wpmu_create_blog() function.

    This function accepts six parameters: 1. $domain — The domain of the new site 2. $path — The path of the new site 3. $title — The title of the new site 4. $user_id — The user ID of the user account who will be the site admin 5. $meta — Additional meta information 6. $site_id — The site ID of the site to be created

    Only the fi rst four parameters are required; the last two are optional. The $site_id parameter is only used if you plan to run multiple WordPress Networks inside a single installation of WordPress. If the new site is created successfully, the function will return the newly created Blog ID of the site.

    For example, you can build a plugin that uses the wpmu_create_blog() function to create new sites in your Multisite network, as follows:

    add_action( 'admin_menu', 'prowp_multisite_create_menu' );

    function prowp_multisite_create_menu() {

    //create custom top-level menu

    c10.indd 270 12/6/12 1:32 AM Coding for Multisite ❘ 271

    add_menu_page( 'Multisite Create Site Page', 'Multisite Create Site', 'manage_options', 'prowp-network-create', 'prowp_multisite_create_sites' );

    }

    First, you’ll create a new top-level menu called Multisite Create Site. This menu will link to a custom function called prowp_multisite_create_sites(), which will allow you to create sites in the net- work. Go ahead and create that function as follows:

    function prowp_multisite_create_sites() { //check if multisite is enabled if ( is_multisite() ) {

    Remember to always verify that Multisite is enabled using the is_multisite() function. Next, you’ll add the code to retrieve the submitted form fi eld values and create a new site in Multisite:

    //if the form was submitted lets process it if ( isset( $_POST['create_site'] ) ) {

    //populate the variables based on form values $domain = strip_tags( $_POST['domain'] ); $path = strip_tags( $_POST['path'] ); $title = strip_tags( $_POST['title'] ); $user_id = absint( $_POST['user_id'] );

    //verify the required values are set if ( $domain && $path && $title && $user_id ) {

    //create the new site in WordPress $new_site = wpmu_create_blog( $domain, $path, $title, $user_id );

    //if successfully display a message if ( $new_site ) {

    echo '

    New site ' .$new_site . ' created successfully!
    ';

    }

    //if required values are not set display an error } else {

    echo '

    New site could not be created. Required fields are missing
    ';

    }

    }

    First check if $_POST['create_site'] is set. This fi eld will be set only if the form has been sub- mitted. Next you’ll populate the variable values with the data submitted via the form. Notice that you’re using the proper sanitizing functions to verify that the values submitted do not contain

    c10.indd 271 12/6/12 1:32 AM 272 ❘ CHAPTER 10 MULTISITE

    HTML and PHP code. You also verify the user_id value is a positive integer using the WordPress absint() function. Now that the variables are set, you want to verify they each have a value. If you are missing data for any of the four required parameters an error message is displayed. After you’ve verifi ed that the values exist, it’s time to execute the wpmu_create_blog() function to create the new site. If the new site is created successfully the variable $new_site will contain the new Blog ID of the site. Now you will build the form for the new site fi elds using the following:

    Create New Site

    This is standard HTML to collect the required fi eld data for creating the new site in Multisite. You can now easily create new sites in your network using the full plugin shown in Listing 10-2.

    LISTING 10-2: Create sites in Multisite example (prowp2-multisite-create-site.zip)

    c10.indd 272 12/6/12 1:32 AM Coding for Multisite ❘ 273

    Version: 1.0 Author: Brad Williams Author URI: http://strangework.com License: GPLv2 */

    add_action( 'admin_menu', 'prowp_multisite_create_menu' );

    function prowp_multisite_create_menu() {

    //create custom top-level menu add_menu_page( 'Multisite Create Site Page', 'Multisite Create Site', 'manage_options', 'prowp-network-create', 'prowp_multisite_create_sites' );

    }

    function prowp_multisite_create_sites() {

    //check if multisite is enabled if ( is_multisite() ) {

    //if the form was submitted let's process it if ( isset( $_POST['create_site'] ) ) {

    //populate the variables based on form values $domain = strip_tags( $_POST['domain'] ); $path = strip_tags( $_POST['path'] ); $title = strip_tags( $_POST['title'] ); $user_id = absint( $_POST['user_id'] );

    //verify the required values are set if ( $domain && $path && $title && $user_id ) {

    //create the new site in WordPress $new_site = wpmu_create_blog( $domain, $path, $title, $user_id );

    //if successfully display a message if ( $new_site ) {

    echo '

    New site ' .$new_site. ' created successfully!
    ';

    }

    //if required values are not set display an error } else {

    echo '

    New site could not be created. Required fields are missing
    ';

    }

    } continues

    c10.indd 273 12/6/12 1:32 AM 274 ❘ CHAPTER 10 MULTISITE

    LISTING 10-2 (continued)

    ?>

    Create New Site

    echo '

    Multisite is not enabled

    ';

    }

    } ?>

    Network Admin Menus Earlier this chapter covered the Multisite Network Admin section of WordPress. This Dashboard is where all Network Settings are confi gured. As in standard WordPress, you can add menus and sub- menus to the Network Admin screen. To do this, you’ll use the network_admin_menu action hook, as shown here:

    add_action( 'network_admin_menu', 'prowp_add_network_settings_menu' );

    c10.indd 274 12/6/12 1:32 AM Coding for Multisite ❘ 275

    The network_admin_menu action hook is triggered after the default network admin menu structure is in place. The second parameter is the custom function prowp_add_network_settings_menu(), which will register your new menu.

    function prowp_add_network_settings_menu() {

    //add settings menu add_menu_page( 'ProWP2 Options Page', 'ProWP2 Options', 'manage_options', 'prowp-network-settings', 'prowp_network_settings' );

    }

    As you can see, registering the new menu is exactly the same as registering a standard WordPress menu. In this example, you use the add_menu_page() function to create a new top-level menu in the Network Admin, as shown in Figure 10-4. Just as easily as you can add a new top-level menu to the Network Admin, you can add submenu items to existing menus. To do this, you’ll use the add_submenu_page() function, as shown here:

    function prowp_add_network_settings_menu() {

    //add Settings submenu add_submenu_page( 'settings.php', 'ProWP2 Options Page', 'ProWP2 Options', 'manage_options', 'prowp-network-settings', 'prowp_network_settings' );

    }

    This function works exactly as in standard WordPress. The fi rst parameter, settings.php in this case, is the most important. That value tells the function what top-level menu to add your submenu to. In this example, you added a sub- menu item called ProWP2 Options to the Settings menu in the Network Admin. The following is a list of fi le names that you can add submenus to:

    ➤ index.php — Add submenu to the Dashboard menu ➤ sites.php — Add submenu to the Sites menu ➤ users.php — Add submenu to the Users menu FIGURE 10-4: Network Admin ➤ themes.php — Add submenu to the Themes menu top-level menu ➤ plugins.php — Add submenu to the Plugins menu ➤ settings.php — Add submenu to the Settings menu ➤ update-core.php — Add submenu to the Updates menu

    As a general rule, it’s best to add your Network Settings as a submenu of the Settings menu in the Network Admin. This is where most users will look for plugin settings, just like in standard WordPress.

    c10.indd 275 12/6/12 1:32 AM 276 ❘ CHAPTER 10 MULTISITE

    Multisite Options When storing options in Multisite, it’s important to use the proper functions to store the options in the proper place. The question you should ask yourself is who should control the settings value. If the setting is specifi c to each site in the network, and can vary between sites, then you should store your options as site options. If the setting should be a network-wide setting that shouldn’t vary between sites, then you should store your options as network options.

    Site Options To store site-specifi c options in Multisite, you can utilize the *_blog_option() functions. The fol- lowing is a list of each function:

    ➤ add_blog_option() — Creates a new option ➤ update_blog_option() — Updates an option and creates it if it doesn’t exist ➤ get_blog_option() — Loads a site option ➤ delete_blog_option() — Deletes a site option

    These functions work almost identically to the standard WordPress option functions; the major dif- ference is that the functions require a $blog_id parameter to be defi ned, as shown here:

    The $key parameter is the option name you want to set and the $value parameter is the value to set for the option.

    Retrieving a site option is just as easy. The following example shows you how to use get_blog_ option() to retrieve site-specifi c options for Blog ID 10:

    Site ID: ' .$blog_id .'

    '; echo '

    Site Name: ' .get_blog_option( $blog_id, 'blogname' ) .'

    '; echo '

    Site URL: ' .get_blog_option( $blog_id, 'siteurl' ) .'

    '; ?>

    Network Options To store network-wide options in Multisite, you can utilize the *_site_option() functions. The follow list describes each function:

    ➤ add_site_option() — Creates a new network option ➤ update_site_option() — Updates a network option and creates it if it doesn’t exist ➤ get_site_option() — Loads a network option ➤ delete_site_option() — Deletes a network option

    c10.indd 276 12/6/12 1:32 AM Coding for Multisite ❘ 277

    These functions work almost identically to standard WordPress option functions, but the option values are stored in the wp_sitemeta Multisite database table. You can use these functions to store global Multisite settings that should be the same for all sites in your network.

    Notice you do not need to defi ne the Blog ID when adding a network option. Because you are stor- ing a network option, it doesn’t matter what Blog ID the code is being executed from.

    If Multisite is not enabled, and your code calls one of the *_site_option() functions, WordPress will fall back to using standard *_option() functions such as add_option().

    Network Options Example Now that you understand how to create and retrieve network options, let’s build a simple network options plugin for Multisite. In this example, you are going to build a plugin to store network wide options. The plugin will degrade gracefully, so if the user is not running Multisite, the options will be stored as standard WordPress options. The fi rst step in your plugin will be to add the Network Settings menu.

    add_action( 'init', 'prowp_network_settings_menu' );

    function prowp_network_settings_menu() {

    if ( is_multisite() ) {

    //Multisite is enabled so add menu to Network Admin add_action( 'network_admin_menu', 'prowp_add_network_settings_menu' );

    } else {

    //Multisite is NOT enabled so add menu to WordPress Admin add_action( 'admin_menu', 'prowp_add_network_settings_menu' );

    }

    }

    The init action hook is used to call your custom function to register the network options menu. Notice how the is_multisite() function is used in the preceding example. If the user has Multisite enabled, the new menu will be added to the Network Admin of Multisite. If the user does not have Multisite enabled, the menu will be added as a standard WordPress menu. This code is important to preserve compatibility regardless of whether Multisite is enabled or not. Now that you’ve registered the proper menu action hook, you need to create the custom function to register the new menu.

    function prowp_add_network_settings_menu() {

    //add settings menu add_menu_page( 'Network Options Page', 'Network Options',

    c10.indd 277 12/6/12 1:32 AM 278 ❘ CHAPTER 10 MULTISITE

    'manage_options', 'prowp-network-settings', 'prowp_network_settings' );

    }

    The preceding code uses the add_menu_page() function to create a new top-level menu labeled Network Options. Now that the menu has been created, you need to create the actual settings form.

    //generate the settings page function prowp_network_settings() { ?>

    Network Settings

    //create nonce hidden field for security wp_nonce_field( 'save-network-settings', 'prowp-network-plugin' ); ?>

    API Key:
    Network Holiday
    Rage Mode: /> Enabled

    c10.indd 278 12/6/12 1:32 AM Coding for Multisite ❘ 279

    You’ll use a standard HTML form to manage the Network Settings. The options will be stored as an array in a single option, as described in the Plugin Settings section of Chapter 8, “Plugin Development.” The fi rst step is to load any existing setting values. Using the get_site_option() function, you’ll load the prowp_network_settings value, which is your options array, if any exists. Next, set each option value into individual variables. Before you actually create your form, use the wp_nonce_field() function to create a hidden form fi eld nonce for security. Now it’s time to build the form. The fi rst form fi eld is an API Key, which is a standard text fi eld. The second form fi eld is a select fi eld. Notice how you use the selected() function to determine which option should be selected. The fi nal setting is a check box for Rage Mode. This option uses the checked() function to determine if the option is checked or not. Because you are using a stan- dard HTML form, you’ll need to add a submit button to submit the form values. Now that your Network Settings form is set up, you’ll need to create the function to process and save the form data.

    add_action( 'admin_init', 'prowp_save_network_settings' );

    //save the option values function prowp_save_network_settings() {

    //if network settings are being saved, process it if ( isset( $_POST['network_settings'] ) ) {

    //check nonce for security check_admin_referer( 'save-network-settings', 'prowp-network-plugin' );

    //store option values in a variable $network_settings = $_POST['network_settings'];

    //use array map function to sanitize option values $network_settings = array_map( 'sanitize_text_field', $network_settings );

    //save option values update_site_option( 'prowp_network_settings', $network_settings );

    }

    }

    You’ll use the admin_init hook for your custom function for saving the form data. The fi rst step is to verify that form values have been posted. If the form wasn’t submitted, there’s nothing for you to process. You’ll do this by verifying that $_POST['network_settings'] is actually set by using the

    c10.indd 279 12/6/12 1:32 AM 280 ❘ CHAPTER 10 MULTISITE

    isset() PHP function. Once you’ve verifi ed that there is form data to process, you need to check your nonce using the check_admin_referer() function.

    Once the nonce check passes, you’ll store the post data in the $network_settings variable. Because the data that you are processing is user-provided, it’s important to sanitize that data before storing it in the database. In this example, you’ll use the array_map() PHP function, which will send each individual value of the array to any function specifi ed, in this case the sanitize_text_field() WordPress function. Now that your data is properly sanitized, you’ll save the option using the update_site_option() function. That’s it! You have just built a fully functional Network Settings section that is fully compatible with standard WordPress. If the user is running Multisite, the menu will show in the Network Admin and the option values will be stored in the wp_sitemeta table. If the user is not running Multisite, the menu will show in the standard WordPress Admin Dashboard and the option values will be stored in the wp_options table. Listing 10-3 shows the fi nalized plugin.

    LISTING 10-3: Multisite network settings (prowp2-multisite-network-settings.zip)

    add_action( 'init', 'prowp_network_settings_menu' );

    function prowp_network_settings_menu() {

    if ( is_multisite() ) {

    //Multisite is enabled so add menu to Network Admin add_action( 'network_admin_menu', 'prowp_add_network_settings_menu' );

    } else {

    //Multisite is NOT enabled so add menu to WordPress Admin add_action( 'admin_menu', 'prowp_add_network_settings_menu' );

    }

    }

    function prowp_add_network_settings_menu() {

    //add settings menu

    c10.indd 280 12/6/12 1:32 AM Coding for Multisite ❘ 281

    add_menu_page( 'Network Options Page', 'Network Options', 'manage_options', 'prowp-network-settings', 'prowp_network_settings' );

    }

    //generate the settings page function prowp_network_settings() { ?>

    Network Settings

    //create nonce hidden field for security wp_nonce_field( 'save-network-settings', 'prowp-network-plugin' ); ?>

    API Key:
    Network Holiday
    Rage Mode: /> Enabled
    continues

    c10.indd 281 12/6/12 1:32 AM 282 ❘ CHAPTER 10 MULTISITE

    LISTING 10-3 (continued)

    add_action( 'admin_init', 'prowp_save_network_settings' );

    //save the option values function prowp_save_network_settings() {

    //if network settings are being saved, process it if ( isset( $_POST['network_settings'] ) ) {

    //check nonce for security check_admin_referer( 'save-network-settings', 'prowp-network-plugin' );

    //store option values in a variable $network_settings = $_POST['network_settings'];

    //use array map function to sanitize option values $network_settings = array_map( 'sanitize_text_field', $network_settings );

    //save option values update_site_option( 'prowp_network_settings', $network_settings );

    }

    }

    Users in a Network When working with users in a Multisite network, you should always verify that a user is a member of a specifi c site. To do this, you’ll use the is_user_member_of_blog() function.

    The function accepts two optional parameters. The fi rst parameter is the user ID of the user you want to check. If not set, the function will check the current user. The second parameter is the Blog ID. If this parameter isn’t set, the function defaults to the current site you are on.

    The preceding code example will verify that the user is a member of the current site they are viewing.

    c10.indd 282 12/6/12 1:32 AM Coding for Multisite ❘ 283

    Now that you understand how to verify that a user is a member of a site, you can add users to a site with the add_user_to_blog() function:

    The function accepts three parameters: 1. $blog_id — The ID of the site you want to add the user to 2. $user_id — The ID of the user to add 3. $role — The role the user will have on the site Now build a plugin that automatically adds a logged-in user to any site that the user visits in your Multisite network, as follows:

    add_action( 'init', 'prowp_multisite_add_user_to_site' );

    First, you’ll use the init action hook to execute your custom function to add users to the site.

    function prowp_multisite_add_user_to_site() {

    //verify user is logged in before proceeding if( !is_user_logged_in() ) return false;

    //load current blog ID and user data global $current_user, $blog_id;

    //verify user is not a member of this site if( ! is_user_member_of_blog() ) {

    //add user to this site as a subscriber add_user_to_blog( $blog_id, $current_user->ID, 'subscriber' );

    }

    }

    The fi rst step is to verify the user is logged in, and if not exit the function by returning false. Next you’ll call the global $current_user and $blog_id variables. These variables store the data of the current logged-in user and the Blog ID the user is currently viewing. Next confi rm that the user is not a member of the current site using the is_user_member_of_blog() function. The fi nal step is to add the user to the site using the add_user_to_blog() function. In this example you set the role of the user to subscriber, but you could easily change this to any role you’d like. That’s it! For this plugin to work across your entire network you’ll either need to Network Activate the plugin or upload to the /mu-plugins directory. Either option will force the plugin to run across all sites in your network. The fi nalized plugin is shown in Listing 10-4.

    c10.indd 283 12/6/12 1:32 AM 284 ❘ CHAPTER 10 MULTISITE

    LISTING 10-4: Automatically add users to sites in Multisite (prowp2-multisite-add-users.zip)

    add_action( 'init', 'prowp_multisite_add_user_to_site' );

    function prowp_multisite_add_user_to_site() {

    //verify user is logged in before proceeding if( !is_user_logged_in() ) return false;

    //load current blog ID and user data global $current_user, $blog_id;

    //verify user is not a member of this site if( ! is_user_member_of_blog() ) {

    //add user to this site as a subscriber add_user_to_blog( $blog_id, $current_user->ID, 'subscriber' );

    }

    } ?>

    Now that you understand how to add users to a site, you can remove users from a site. To remove users you’ll use the remove_user_from_blog() function:

    This function accepts three parameters: 1. $user_id — ID of the user you want to remove 2. $blog_id — ID of the blog to remove the user from 3. $reassign — ID of a user to reassign posts to

    The $user_id and $blog_id parameters are required. The $reassign parameter is optional. This parameter should be the ID of the user you want to reassign posts to when removing a user.

    c10.indd 284 12/6/12 1:32 AM Coding for Multisite ❘ 285

    NOTE Remember that adding and removing users from a site in Multisite is not actually creating or deleting the user in WordPress, but instead adding or remov- ing them as a member of a specifi c site.

    To retrieve a list of all sites a user belongs to you’ll use the get_blogs_of_user() function. This function returns an array of objects containing the details of each site the user has access to. Here’s an example:

    echo 'User '.$user_id.'\'s blogs:

      ';

      foreach ( $user_blogs AS $user_blog ) {

      echo '

    • ' .$user_blog->blogname .'
    • ';

      }

      echo '

    '; ?>

    The preceding code retrieves the site data for all sites that user ID 1 is a member of. You then loop through the returned array, displaying the blogname value for each site.

    Super Admins Earlier this chapter covered the new user role introduced in Multisite, the Super Admin role. Any user set to the Super Admin role has full control over every site in your Multisite network. Users set to the Super Admin role also have full control over what themes and plugins are available, all users, and network-wide settings.

    To retrieve a list of all Super Admins in Multisite you’ll use the get_super_admins() function. This function accepts no parameters and returns an array of all Super Admin usernames in your net- work. Here’s an example:

    The preceding code example would return the following array of Super Admins: Array

    ( [0] => admin [1] => michael_myers )

    c10.indd 285 12/6/12 1:32 AM 286 ❘ CHAPTER 10 MULTISITE

    You can also check a specifi c user ID to determine if this user is a Super Admin in your network. To do so, use the is_super_admin() function, as shown here:

    if ( is_super_admin( $user_id ) ) { echo 'User is a Super Admin'; } ?>

    The preceding code example checks if User ID 1 is a Super Admin. The function accepts a $user_id as an optional parameter. If the user ID isn’t passed to the function, it defaults to the current user. Now that you understand how to check for Super Admins, you can make a user a Super Admin. You can easily assign an existing user to the Super Admin role by using the grant_super_admin() func- tion. This function accepts a single required parameter, which is the user ID you want to add to the Super Admin role.

    You can also easily remove a user from the Super Admin role with the revoke_super_admin() function. As in the preceding code, this function accepts a single parameter, which is the user ID you want to remove from the Super Admin role:

    Both of these functions are located in wp-admin/includes/ms.php. This means that these functions are not available on the public side of your website and can only be used on the admin side.

    Network Stats Multisite features various functions to generate stats about your network. The get_blog_count() function returns the total number of sites in your network. To return the total number of users in your network, use the get_user_count() function.

    echo '

    Total sites: ' .$site_count .'

    '; echo '

    Total users: ' .$user_count .'

    '; ?>

    c10.indd 286 12/6/12 1:32 AM Multisite Database Schema ❘ 287

    You can also use the get_sitestats() function to retrieve both values at once in an array.

    echo '

    Total sites: ' .$network_stats['blogs'] .'

    '; echo '

    Total users: ' .$network_stats['users'] .'

    '; ?>

    MULTISITE DATABASE SCHEMA

    WordPress Multisite features a different database schema from standard WordPress. When enabling Multisite, WordPress creates the necessary tables in your database to support Multisite functionality.

    Multisite-Specifi c Tables WordPress stores global Multisite settings in centralized tables. These tables are created only when Multisite is enabled and installed, excluding the wp_users and wp_usermeta tables, which exist in standard WordPress.

    ➤ wp_blogs — Contains each site created in Multisite. ➤ wp_blog_versions — Contains the current database version of each site in the network. ➤ wp_registration_log — A log of all users registered and activated in WordPress. ➤ wp_signups — Contains users and sites registered using the WordPress registration process. ➤ wp_site — Contains the primary site’s address information. ➤ wp_sitecategories — Contains global terms. Only exists if global terms have been enabled in WordPress. ➤ wp_sitemeta — Contains option data for the network, including Super Admin accounts. ➤ wp_users — Contains all users registered in WordPress. ➤ wp_usermeta — Contains all metadata for user accounts in WordPress.

    As you have probably noticed, some important WordPress tables are missing. The rest of the tables created for Multisite are site-specifi c.

    Site-Specifi c Tables Every site in your network features its own set of site-specifi c database tables. These tables contain the content and settings specifi c to that individual site. Remember that these tables are prefi xed with the $table_prefix value defi ned in wp-config.php, followed by the $blog_id and then the table name.

    c10.indd 287 12/6/12 1:32 AM 288 ❘ CHAPTER 10 MULTISITE

    ➤ wp_2_commentmeta

    ➤ wp_2_comments

    ➤ wp_2_links

    ➤ wp_2_options

    ➤ wp_2_postmeta

    ➤ wp_2_posts

    ➤ wp_2_terms

    ➤ wp_2_term_relationships

    ➤ wp_2_term_taxonomy

    Every time you create a new site in your Multisite network, WordPress will create the preceding nine tables in your database for the new site. As you can see, these tables can make your database quickly grow in size. That’s why the only limitation to WordPress Multisite is the server resources available for your network of sites. If your network contains 1,000 sites, your database would have more than 9,000 tables. Obviously a network of this size would not work well on a small, shared hosting account. In Chapter 6, “Data Management,” you covered the importance of using the WordPress database class when querying the database directly. This is especially important in Multisite since the table prefi x contains the Blog ID of the site you are viewing. When writing a custom query, you should always prefi x the table reference with $wpdb->, which will include the site ID if you are running Multisite. As an example, $wpdb->posts would query the wp_2_posts table above, assuming you are working on Blog ID 2 in your network.

    SUMMARY

    WordPress Multisite is an amazing feature of WordPress with limitless possibilities. Now that Multisite is a core WordPress feature, many users are converting their standard WordPress website to a Multisite network to take advantage of the rapid site deployment features and network capabili- ties. As more and more users are becoming familiar with the power of Multisite, its use is growing at a very rapid pace. When developing for WordPress, it’s very important to think about Multisite and how your code can utilize these powerful Multisite features covered in this chapter. As a WordPress user, it’s also very important to verify the themes and plugins you are using are Multisite compatible. In the next chapter you’ll cover Content Aggregation. You’ll learn how to work with external APIs to import data from various sources into your website, social media button integration, and under- standing different advertising methods and how to monetize your website.

    c10.indd 288 12/6/12 1:32 AM 11 Content Aggregation

    WHAT’S IN THIS CHAPTER?

    ➤ Getting your content noticed ➤ Importing various sources into your WordPress site ➤ Using WordPress to cache remote content ➤ Understanding diff erent advertising methods to monetize your website When we began writing this book three years ago, “content aggregation” was viewed as a set of mechanisms to keep your WordPress site updated with information scattered across a rapidly growing number of social networks, and to feed WordPress content updates into those same networks. Sometimes WordPress was the source, sometimes the destination, but the focus was on moving content around — and in so doing, a bit of advice was overlooked in Chapter 1, namely, that sometimes context is as important as the actual content. “Content aggregation” is about how and where you want to direct your audience, and for what purposes. A brief look at what’s changed between editions sets the context for this: Facebook has reached close to a billion users. Twitter sends hashtags in trending motion, and most large content sites have vanity URL shorteners to supplant bit.ly. Google, Bing, and Yahoo are the somewhat lopsided three legs of search. There’s one invariant over time: your WordPress site represents the sum total of your exper- tise, personal brand, curated content, and design. It is the thing you put in a registration form fi eld labeled “Personal Website” or “Business Website,” and that distinction may help shape the way you manage content aggregation. If your website is about you as an individual, and you see WordPress as an additional vehicle to reach your audience, then you’ll be pulling con- tent in from other sources to populate your WordPress site. Your goal typically is to grow an audience, with less concern for where that audience resides on the web. On the other hand, if your website is about your business or your personal brand as a consultant or expert, or even

    c11.indd 289 12/6/12 1:32 AM 290 ❘ CHAPTER 11 CONTENT AGGREGATION

    just a landing page for valuable advertising, then you’ll want to focus your audience on your own website. Facebook and Twitter are additional conduits for attracting readers whom you’ll want to click through to your WordPress site. As you evaluate the inbound and outbound aggregation methods in this chapter, think about their effect on reaching your audience: Is WordPress the only channel, or is it one of many channels? This chapter discusses pulling content from external locations — typically social networking destinations — into your website, and conversely, taking your own content and publishing it through social sites or feeds. In particular, you will cover YouTube, Twitter, Facebook, and sub- scription feeds, since they are the most common. You’ll also consider how to utilize other content sites with their own best of breed and open APIs such as Google Maps, and how you can use the WordPress API to integrate other sources. You’ll start with leveraging social networks to get your site and content noticed through outbound content aggregation.

    GETTING NOTICED

    Nearly all websites exist so that visitors will transact business, whether that business involves goods, services, or your own brand. Before any transaction can happen, however, people have to be able to fi nd your website. This section is about promoting that brand and your site on the web through con- tent sharing and social networks, not search engine optimization or “SEO.” SEO and getting your site discovered are covered in Chapter 12. Promoting your online identity is one of the major reasons to amass your online interactions into one place. You can collect all of your social media interactions on your website to showcase your professional involvement in a community or profession. This can highlight your expertise in one or more specifi c areas as well as expand your potential audience to different groups. It can really func- tion as a type of business networking among different potential readership groups. Even if you’re not using a public persona for business purposes, the same goal of centralizing all of your online activities amplifi es the benefi ts to your hobbies or your personal passion. If you partici- pate in social networks for home beer-brewing, why not aggregate those activities into one location? If you attract attention because of your witty insight or accurate and knowledgeable information, aggregation is one way to become recognized as an expert in your fi eld of interest. A nice side effect of this aggregation effect is that the larger the number of links that point back to your website, the more the popular search engines will fi nd your site, as covered in Chapter 12. Collecting information from multiple sites into your WordPress site makes it easier for others to fi nd that information. Your readers or potential audience don’t have to keep tabs on all the different places in which your updates could be broadcast or shared. In the same vein, how will clients know to check your latest YouTube promotional video if they do not know it is even available? Collecting this information into a primary source brings all these different data points in front of your audi- ence’s eyes through content aggregation. And in the end, it drives traffi c to your site rather than away from it, because your site becomes the one true source. This is a classic long tail content problem, and is worth discussing a little more. Your website is just one source of content in hundreds of millions out there. But the people you intersect and actively

    c11.indd 290 12/6/12 1:32 AM Getting Noticed ❘ 291

    communicate with, and the set of those people’s friends and families (the “closure of the set” if you’re an honorary advanced math geek), builds an audience: your audience. Aggregating your content is about building this audience by showing up in multiple places with appropriate content, context, and granularity of updates. Tweeting about recent blog posts, or importing blog post sum- maries into Facebook, for example, are easy ways to spread the word. Incorporating professional organizations that strengthen your own professional reputation furthers this goal. Furthermore aggregation is not simply talking about accumulating social network content but also using websites that provide a functionality, geophysical content or a service that is incorporated into your website. Google Maps is an obvious addition so clients and customers can fi nd your business; so is including Twitter discussions by or about your brand or containing a relevant hashtag. In the end, having more functionality on your site is still marketing. You are either trying to get your site to be more full featured and attractive to your potential clients and customers, or you are leveraging a third-party content source to provide some value.

    Social Media Buttons Marketing is marketing — somehow you have to get your website noticed. A great way — perhaps the best way — to get your content noticed and generate more traffi c to your site is to use the power of the social networking sites. First, you have to have good, interesting content on your site. But unlike Field of Dreams, if you built it, they will not necessarily come. You have to advertise. Of course, an ambitious and loyal visitor may take the link to your content and submit it to the Internet at large, but why not make it even easier? Adding social media buttons to your WordPress site makes it easy for readers to include your content in their rankings, ratings, or aggregations, or simply to broadcast to their audience, “Hey people, I like this.” Getting consumers to share their preferences and point back to your content is the core idea of the “long tail” of content; without recommendations from similarly like-minded people, your content never gets discovered. This is true for music, movies, or blogs. So why not make it a one-click event to share your content on your visitors’ own social streams?

    The ShareThis plugin (http://wordpress.org/extend/plugins/share-this/) does just this. The Sociable plugin supports links to nearly 120 different social networking channels for your visitors to share your content. This plugin is confi gurable and has a decent control panel. You can confi gure which sites are enabled for sharing, allowing you to tailor the des- tination site list for your audience or just your own preferences. There are also several options for controlling the rendering of the social networking icons on your site, as well as some additional styling options. The default options provide a nice sharing FIGURE 11-1: ShareThis social networking button under a snippet at the bottom of each post, as shown WordPress post in Figure 11-1.

    c11.indd 291 12/6/12 1:32 AM 292 ❘ CHAPTER 11 CONTENT AGGREGATION

    Most admirable about this plugin is the simplicity. Out of the box, it just works, and it works as advertised. Visitors can read some of your great content and decide to share it on Pinterest. They simply need to click the Pinterest icon and log in to Pinterest. It is that simple.

    Feeding WordPress Upstream If you adopt the notion that your WordPress site is the eventual destination for your audience, then your presence on Facebook, Twitter, and Pinterest is “upstream” from that click-through target. While you benefi t from readers sharing content via the social media buttons, you can also feed entire posts, lead-ins, or post titles to your other online profi les. Getting your updates on Twitter can be as simple or complex as you like. In the corner case simply tweet about your latest post, using a URL shortener to open up tweet room for hashtags, com- ments, or a witty cross-reference. If you prefer to have your Twitter account announce each new post on your WordPress site, check out a plugin such as Tweetily (http://wordpress.org/extend/ plugins/tweetily-tweet-wordpress-posts-automatically/) to construct tweets from post titles and your introductory or hashtag text. Sending your post content off to Facebook prompts another “where do I want my audience to end up” thought exercise. If you automatically re-post WordPress content into your Facebook newsfeed, then the Facebook audience has no reason or need to come back to your WordPress site, and you may lose commenters, readers, or possible click-throughs to other content. You’re effectively frag- menting your audience by sharing content, however counterintuitive that seems. On the other hand, if your Facebook audience is a source of new readers who may discover you via a friend of a friend or because someone “liked” your post, then feeding your posts up to Facebook can add to your audience. There’s a middle ground if you decide to create a Facebook page for your WordPress site, neatly working around the bidirectional nature of “friend” relationships and the limit on the num- ber of friend connections you can have. Whether it’s a page or your personal account, the simplest way to get content from your WordPress site into Facebook is via the Facebook RSS Graffi ti application (http://apps.facebook.com/rss- graffiti/). Within Facebook, install the application, and then point it at the RSS feed of your site, which is of the form example.com/feed/rss2. The RSS Graffi ti application is confi gurable in terms of how frequently it checks the RSS feed for updates and the newsfeed header used to preface each update; most important, you can confi gure if you want to import the entire post or just an excerpt. If you send excerpts from WordPress to Facebook, then Facebook users will get a whiff of your content but will have to click through to your WordPress site to savor the whole content disk. If you’re concerned about splitting your audience, use excerpts; if you’re trying to grow your pres- ence on multiple social networks, send entire posts upstream.

    Buttons, Badges, or Both? As you can see, there are two equally important goals to sharing content. The fi rst is taking your con- tent and spreading it out to the social networks, such as Facebook, Twitter, and Pinterest, which has been covered in this section so far. The second is linking to your own personal profi les on these sites from your own site, effectively showing the reader what’s on the other side of “Follow me on Twitter” or “Find us on Facebook” badges you might place in a WordPress widget in one of your sidebars.

    c11.indd 292 12/6/12 1:32 AM Simple Social Networking Badges ❘ 293

    Linking to your social networking profi les from your own site reinforces the idea of using your site as the hub. It validates your profi les as being truly yours and representative of your online voice. Connecting these profi les together is a double-edged sword because not only does it certify your profi les and solidify your online reputation, but it also drives readers from one site to your profi les or destination at another, potentially fragmenting your audience. Which direction your drive traffi c goes is a choice you will have to make, which is pretty much the same caution expressed at the out- set of this chapter. Validating your community profi les is a great thing if you are using your online presence for a professional endeavor, whether personally or for as a business entity. It’s advertising how involved you are and in what capacities. Affi rming your identities from your main site vets those other online presences like a trust system. This relieves any doubt your visitor may have, and you do not have to deal with Twitter “Verifi ed Accounts.” And you don’t have to change your name to @ theRealDavidDamstraHonest. You can link to your profi les pretty easily by editing the template fi les or using an HTML widget. In general, these links do not change very often, if at all. Nevertheless, some nice plugins handle all of the hard work for you.

    For example, the Social Media Widget plugin (http://wordpress.org/ extend/plugins/social-media-widget/) by Brian Freytag supports many social media sites. Once installed, drag the widget into the appropriate side- bar in the control panel. Confi gure the widget using whichever social media URLs you are publicizing. Figure 11-2 shows an example of the widget. FIGURE 11-2: Social In addition to all of the built-in social networking sites, this plugin allows Media Widget for up to six arbitrarily customized ones and also the ability to use your own sidebar widget custom icons. It is a nice way to centralize all of your online presences into one easy-to-manage page. Many plugins can achieve similar results. Check them out and make your own evaluation.

    SIMPLE SOCIAL NETWORKING BADGES

    There are two different ways to integrate external social networking sites into your own site, and they have very different impacts on your content. You want to be able to distinguish between the two and to be able to decide when you need one over the other. The fi rst is what is called “simple social networking badges.” These are snapshots of your participation in an external site at a specifi c moment in time, and most can be auto-generated by the destination site (usually behind a button that prompts you to “Add a badge!”) Badge-based content is ephemeral and changes as frequently as you log activity on the external site. For example, these badges could be sidebar widgets showing your latest tweet or your current Facebook status. These simple social networking badges showcase your membership in other social sites, but they do not contribute to the content on your own site. They are more decorative than substantive, even if your participation on the other site is extensive. As mentioned previously, if you are much more active on Twitter, for example, than on updating your blog, then including your Twitter badge may cause some readers to migrate to your Twitter page to follow your online presence.

    c11.indd 293 12/6/12 1:32 AM 294 ❘ CHAPTER 11 CONTENT AGGREGATION

    The second possibility is actually to take the content, or a portion of it, from the external location and republish it on your WordPress site. This gives the content a life of its own, and on your own terms. Specifi cally, you are not relying on the third party to preserve your content, but are making your own copy in your own database, making it your personal responsibility to maintain. This is often called “owning your data.” Both the snapshot and republishing use cases have value, but you should recognize the difference in the permanence and longevity of the different approaches. This chapter covers both use cases. Using social networking badges creates a positive feedback loop around your WordPress site. Ideally, if you get people following you on Twitter or Facebook, they’re more likely to click through to your listed website to read your output in longer and larger form. People who read your site may also want to follow your shorter or unrelated updates on other sites; in both cases you’re relying on the network effort of “friends of friends” to drive interest in your content.

    COLLECTING EXTERNAL CONTENT

    You have weighed the pros and cons of integrating external content into your WordPress site. The next question is how are you going to do it? And which sites are you going to include? Basically, you can only include sites that have an API to permit the consumption of the data. That is not entirely true; you could code up a spider that logs into the remote site and harvests the informa- tion you want, but that would be pretty obscure and not fun to maintain long-term. For the pur- poses of this book, you are going to focus on exposed APIs. The basic method to integrate services is to read the API documentation and create a plugin or other function to consume and convert the information into something WordPress can use, such as a post. Because the underlying architecture of WordPress is PHP, you can use whatever PHP tricks you have up your sleeve and code a nice solution. For more information about plugin development, see Chapter 8. In truth, you do not even have to use PHP; you could use an intermediary language and interface with the WordPress table directly. For the sake of this section, you will focus on building a business site that will leverage social net- works and other websites that provide functionality that can be embedded into your own site to add value or context. While you could build some these features yourself, the build-versus-buy decision becomes pretty easy on the open web, when essentially the cost is zero. Sure, there are business con- siderations about relying on third parties and including third-party code into your own business site, but at this time in the web’s development, users are more and more informed and aware that this is how websites work and they accept this functionality. Starting with WordPress 2.9, WordPress has included functionality for oEmbed. oEmbed is a means for one website to integrate a code snippet from another. The term “integrate” is used pretty loosely here because the HTML could be an